1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
/* |
6
|
|
|
* This file is part of the Valkyrja Framework package. |
7
|
|
|
* |
8
|
|
|
* (c) Melech Mizrachi <[email protected]> |
9
|
|
|
* |
10
|
|
|
* For the full copyright and license information, please view the LICENSE |
11
|
|
|
* file that was distributed with this source code. |
12
|
|
|
*/ |
13
|
|
|
|
14
|
|
|
namespace Valkyrja\Test\Output\Formatters; |
15
|
|
|
|
16
|
|
|
use Valkyrja\Application\Application; |
17
|
|
|
use Valkyrja\Test\Exceptions\AssertFailureException; |
18
|
|
|
use Valkyrja\Test\Output\Formatter as Contract; |
19
|
|
|
use Valkyrja\Test\Output\Results; |
20
|
|
|
use Valkyrja\Test\Test; |
21
|
|
|
|
22
|
|
|
use function count; |
23
|
|
|
use function strlen; |
24
|
|
|
|
25
|
|
|
use const PHP_EOL; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Class Formatter. |
29
|
|
|
* |
30
|
|
|
* @author Melech Mizrachi |
31
|
|
|
*/ |
32
|
|
|
class Formatter implements Contract |
33
|
|
|
{ |
34
|
|
|
/** |
35
|
|
|
* @inheritDoc |
36
|
|
|
*/ |
37
|
|
|
public function title(): string |
38
|
|
|
{ |
39
|
|
|
return "Valkyrja Unit Testing {$this->getTitleVersion()} by Melech Mizrachi and contributors."; |
40
|
|
|
} |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* @inheritDoc |
44
|
|
|
*/ |
45
|
|
|
public function meta(): string |
46
|
|
|
{ |
47
|
|
|
return "Time: {$this->getMetaTime()}, Memory: {$this->getMetaMemory()}"; |
48
|
|
|
} |
49
|
|
|
|
50
|
|
|
/** |
51
|
|
|
* @inheritDoc |
52
|
|
|
*/ |
53
|
|
|
public function tests(Results $results): string |
54
|
|
|
{ |
55
|
|
|
$tests = $results->getTests(); |
56
|
|
|
|
57
|
|
|
$testsFormatted = ''; |
58
|
|
|
|
59
|
|
|
foreach ($tests as $test) { |
60
|
|
|
if ($testsFormatted !== '' && (strlen($testsFormatted) % 80) === 0) { |
61
|
|
|
$testsFormatted .= PHP_EOL; |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
$assert = $test->getAssert(); |
65
|
|
|
$result = $this->getTestSuccess(); |
66
|
|
|
|
67
|
|
|
if ($assert->warnings) { |
|
|
|
|
68
|
|
|
$result = $this->getTestWarning(); |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
if ($assert->errors) { |
|
|
|
|
72
|
|
|
$result = $this->getTestError(); |
73
|
|
|
} |
74
|
|
|
|
75
|
|
|
if ($test->shouldSkip()) { |
76
|
|
|
$result = $this->getTestSkip(); |
77
|
|
|
} |
78
|
|
|
|
79
|
|
|
$testsFormatted .= $result; |
80
|
|
|
} |
81
|
|
|
|
82
|
|
|
return $testsFormatted; |
83
|
|
|
} |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* @inheritDoc |
87
|
|
|
*/ |
88
|
|
|
public function completed(Results $results): string |
89
|
|
|
{ |
90
|
|
|
$tests = $results->getTests(); |
91
|
|
|
$total = count($tests); |
92
|
|
|
$failed = 0; |
93
|
|
|
|
94
|
|
|
foreach ($tests as $test) { |
95
|
|
|
$assert = $test->getAssert(); |
96
|
|
|
|
97
|
|
|
if ($assert->errors) { |
|
|
|
|
98
|
|
|
$failed++; |
99
|
|
|
} |
100
|
|
|
} |
101
|
|
|
|
102
|
|
|
$count = $total - $failed; |
103
|
|
|
$percentPassed = ($count / $total) * 100; |
104
|
|
|
|
105
|
|
|
return $this->getCompleted($count, $total, $percentPassed); |
106
|
|
|
} |
107
|
|
|
|
108
|
|
|
/** |
109
|
|
|
* @inheritDoc |
110
|
|
|
*/ |
111
|
|
|
public function results(Results $results): string |
112
|
|
|
{ |
113
|
|
|
$tests = $results->getTests(); |
114
|
|
|
$status = $this->getResultsOk(); |
115
|
|
|
|
116
|
|
|
$totalTests = count($tests); |
117
|
|
|
$totalErrors = 0; |
118
|
|
|
$totalSkipped = 0; |
119
|
|
|
$totalWarnings = 0; |
120
|
|
|
$totalAssertions = 0; |
121
|
|
|
|
122
|
|
|
foreach ($tests as $test) { |
123
|
|
|
$assert = $test->getAssert(); |
124
|
|
|
|
125
|
|
|
$totalAssertions += count($assert->assertions); |
126
|
|
|
|
127
|
|
|
if ($assert->warnings) { |
|
|
|
|
128
|
|
|
$status = $this->getResultsWarning(); |
129
|
|
|
|
130
|
|
|
$totalWarnings++; |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
if ($assert->errors) { |
|
|
|
|
134
|
|
|
$status = $this->getResultsError(); |
135
|
|
|
|
136
|
|
|
$totalErrors++; |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
if ($test->shouldSkip()) { |
140
|
|
|
$totalSkipped++; |
141
|
|
|
} |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
return $this->getResults($status, $totalTests, $totalAssertions, $totalSkipped, $totalWarnings, $totalErrors); |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
/** |
148
|
|
|
* @inheritDoc |
149
|
|
|
*/ |
150
|
|
|
public function issues(Results $results): string |
151
|
|
|
{ |
152
|
|
|
$tests = $results->getTests(); |
153
|
|
|
$issues = []; |
154
|
|
|
$num = 1; |
155
|
|
|
|
156
|
|
|
foreach ($tests as $test) { |
157
|
|
|
$assert = $test->getAssert(); |
158
|
|
|
|
159
|
|
|
if ($assert->errors) { |
|
|
|
|
160
|
|
|
$error = $assert->errors[0]; |
161
|
|
|
$issues[] = $this->getIssuesIssue($num, $test, $error); |
162
|
|
|
|
163
|
|
|
$num++; |
164
|
|
|
} |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
if (empty($issues)) { |
168
|
|
|
return $this->getIssuesBlank(); |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
return $this->getIssues($issues); |
172
|
|
|
} |
173
|
|
|
|
174
|
|
|
/** |
175
|
|
|
* @inheritDoc |
176
|
|
|
*/ |
177
|
|
|
public function sectionSpacing(): string |
178
|
|
|
{ |
179
|
|
|
return PHP_EOL . PHP_EOL; |
180
|
|
|
} |
181
|
|
|
|
182
|
|
|
/** |
183
|
|
|
* Get the title's version formatted. |
184
|
|
|
*/ |
185
|
|
|
protected function getTitleVersion(): string |
186
|
|
|
{ |
187
|
|
|
return Application::VERSION; |
188
|
|
|
} |
189
|
|
|
|
190
|
|
|
/** |
191
|
|
|
* Get the meta's time formatted. |
192
|
|
|
*/ |
193
|
|
|
protected function getMetaTime(): string |
194
|
|
|
{ |
195
|
|
|
return '0'; |
196
|
|
|
} |
197
|
|
|
|
198
|
|
|
/** |
199
|
|
|
* Get the meta's memory formatted. |
200
|
|
|
*/ |
201
|
|
|
protected function getMetaMemory(): string |
202
|
|
|
{ |
203
|
|
|
return (string) memory_get_peak_usage(); |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
/** |
207
|
|
|
* Get the test's success formatted. |
208
|
|
|
*/ |
209
|
|
|
protected function getTestSuccess(): string |
210
|
|
|
{ |
211
|
|
|
return '.'; |
212
|
|
|
} |
213
|
|
|
|
214
|
|
|
/** |
215
|
|
|
* Get the test's warning formatted. |
216
|
|
|
*/ |
217
|
|
|
protected function getTestWarning(): string |
218
|
|
|
{ |
219
|
|
|
return 'W'; |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
/** |
223
|
|
|
* Get the test's error formatted. |
224
|
|
|
*/ |
225
|
|
|
protected function getTestError(): string |
226
|
|
|
{ |
227
|
|
|
return 'E'; |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
/** |
231
|
|
|
* Get the test's skip formatted. |
232
|
|
|
*/ |
233
|
|
|
protected function getTestSkip(): string |
234
|
|
|
{ |
235
|
|
|
return 'S'; |
236
|
|
|
} |
237
|
|
|
|
238
|
|
|
/** |
239
|
|
|
* Get the completed full text formatted. |
240
|
|
|
*/ |
241
|
|
|
protected function getCompleted(int $count, int $total, int $percentPassed): string |
242
|
|
|
{ |
243
|
|
|
return "{$this->getCompletedCount($count)} / {$this->getCompletedTotal($total)}" |
244
|
|
|
. " ({$this->getCompletedPercentPassed($percentPassed)}%) Completed"; |
245
|
|
|
} |
246
|
|
|
|
247
|
|
|
/** |
248
|
|
|
* Get the completed's count formatted. |
249
|
|
|
*/ |
250
|
|
|
protected function getCompletedCount(int $count): string |
251
|
|
|
{ |
252
|
|
|
return (string) $count; |
253
|
|
|
} |
254
|
|
|
|
255
|
|
|
/** |
256
|
|
|
* Get the completed's total formatted. |
257
|
|
|
*/ |
258
|
|
|
protected function getCompletedTotal(int $total): string |
259
|
|
|
{ |
260
|
|
|
return (string) $total; |
261
|
|
|
} |
262
|
|
|
|
263
|
|
|
/** |
264
|
|
|
* Get the completed's percent passed formatted. |
265
|
|
|
*/ |
266
|
|
|
protected function getCompletedPercentPassed(int $percentPassed): string |
267
|
|
|
{ |
268
|
|
|
return (string) $percentPassed; |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
/** |
272
|
|
|
* Get the results' ok status formatted. |
273
|
|
|
*/ |
274
|
|
|
protected function getResultsOk(): string |
275
|
|
|
{ |
276
|
|
|
return 'OK'; |
277
|
|
|
} |
278
|
|
|
|
279
|
|
|
/** |
280
|
|
|
* Get the results' warning status formatted. |
281
|
|
|
*/ |
282
|
|
|
protected function getResultsWarning(): string |
283
|
|
|
{ |
284
|
|
|
return 'Warning'; |
285
|
|
|
} |
286
|
|
|
|
287
|
|
|
/** |
288
|
|
|
* Get the results' error status formatted. |
289
|
|
|
*/ |
290
|
|
|
protected function getResultsError(): string |
291
|
|
|
{ |
292
|
|
|
return 'Error'; |
293
|
|
|
} |
294
|
|
|
|
295
|
|
|
/** |
296
|
|
|
* Get the results full text formatted. |
297
|
|
|
*/ |
298
|
|
|
protected function getResults(string $status, int $totalTests, int $totalAssertions, int $totalSkipped, int $totalWarnings, int $totalErrors): string |
299
|
|
|
{ |
300
|
|
|
return $status |
301
|
|
|
. ' (' |
302
|
|
|
. $this->getResultsTotalTests($totalTests) |
303
|
|
|
. $this->getResultsTotalAssertions($totalAssertions) |
304
|
|
|
. $this->getResultsTotalErrors($totalErrors) |
305
|
|
|
. $this->getResultsTotalSkipped($totalSkipped) |
306
|
|
|
. $this->getResultsTotalWarnings($totalWarnings) |
307
|
|
|
. ')'; |
308
|
|
|
} |
309
|
|
|
|
310
|
|
|
/** |
311
|
|
|
* Get the results full text total tests formatted. |
312
|
|
|
*/ |
313
|
|
|
protected function getResultsTotalTests(int $totalTests): string |
314
|
|
|
{ |
315
|
|
|
return "{$this->getResultsTotalTestsCount($totalTests)} {$this->getResultsTotalTestsGrammar($totalTests)}"; |
316
|
|
|
} |
317
|
|
|
|
318
|
|
|
/** |
319
|
|
|
* Get the results full text total tests count formatted. |
320
|
|
|
*/ |
321
|
|
|
protected function getResultsTotalTestsCount(int $totalTests): string |
322
|
|
|
{ |
323
|
|
|
return (string) $totalTests; |
324
|
|
|
} |
325
|
|
|
|
326
|
|
|
/** |
327
|
|
|
* Get the results full text total tests grammar formatted. |
328
|
|
|
*/ |
329
|
|
|
protected function getResultsTotalTestsGrammar(int $totalTests): string |
330
|
|
|
{ |
331
|
|
|
return $totalTests === 1 |
332
|
|
|
? 'test' |
333
|
|
|
: 'tests'; |
334
|
|
|
} |
335
|
|
|
|
336
|
|
|
/** |
337
|
|
|
* Get the results full text total assertions formatted. |
338
|
|
|
*/ |
339
|
|
|
protected function getResultsTotalAssertions(int $totalAssertions): string |
340
|
|
|
{ |
341
|
|
|
return ", {$this->getResultsTotalAssertionsCount($totalAssertions)} {$this->getResultsTotalAssertionsGrammar($totalAssertions)}"; |
342
|
|
|
} |
343
|
|
|
|
344
|
|
|
/** |
345
|
|
|
* Get the results full text total assertions count formatted. |
346
|
|
|
*/ |
347
|
|
|
protected function getResultsTotalAssertionsCount(int $totalAssertions): string |
348
|
|
|
{ |
349
|
|
|
return (string) $totalAssertions; |
350
|
|
|
} |
351
|
|
|
|
352
|
|
|
/** |
353
|
|
|
* Get the results full text total assertions grammar formatted. |
354
|
|
|
*/ |
355
|
|
|
protected function getResultsTotalAssertionsGrammar(int $totalAssertions): string |
356
|
|
|
{ |
357
|
|
|
return $totalAssertions === 1 |
358
|
|
|
? 'assertion' |
359
|
|
|
: 'assertions'; |
360
|
|
|
} |
361
|
|
|
|
362
|
|
|
/** |
363
|
|
|
* Get the results full text total errors formatted. |
364
|
|
|
*/ |
365
|
|
|
protected function getResultsTotalErrors(int $totalErrors): string |
366
|
|
|
{ |
367
|
|
|
if ($totalErrors <= 0) { |
368
|
|
|
return ''; |
369
|
|
|
} |
370
|
|
|
|
371
|
|
|
return ", {$this->getResultsTotalErrorsCount($totalErrors)} {$this->getResultsTotalErrorsGrammar($totalErrors)}, "; |
372
|
|
|
} |
373
|
|
|
|
374
|
|
|
/** |
375
|
|
|
* Get the results full text total errors count formatted. |
376
|
|
|
*/ |
377
|
|
|
protected function getResultsTotalErrorsCount(int $totalErrors): string |
378
|
|
|
{ |
379
|
|
|
return (string) $totalErrors; |
380
|
|
|
} |
381
|
|
|
|
382
|
|
|
/** |
383
|
|
|
* Get the results full text total errors grammar formatted. |
384
|
|
|
*/ |
385
|
|
|
protected function getResultsTotalErrorsGrammar(int $totalErrors): string |
386
|
|
|
{ |
387
|
|
|
return $totalErrors === 1 |
388
|
|
|
? 'error' |
389
|
|
|
: 'errors'; |
390
|
|
|
} |
391
|
|
|
|
392
|
|
|
/** |
393
|
|
|
* Get the results full text total warnings formatted. |
394
|
|
|
*/ |
395
|
|
|
protected function getResultsTotalWarnings(int $totalWarnings): string |
396
|
|
|
{ |
397
|
|
|
if ($totalWarnings <= 0) { |
398
|
|
|
return ''; |
399
|
|
|
} |
400
|
|
|
|
401
|
|
|
return ", {$this->getResultsTotalWarningsCount($totalWarnings)} {$this->getResultsTotalWarningsGrammar($totalWarnings)}, "; |
402
|
|
|
} |
403
|
|
|
|
404
|
|
|
/** |
405
|
|
|
* Get the results full text total warnings count formatted. |
406
|
|
|
*/ |
407
|
|
|
protected function getResultsTotalWarningsCount(int $totalWarnings): string |
408
|
|
|
{ |
409
|
|
|
return (string) $totalWarnings; |
410
|
|
|
} |
411
|
|
|
|
412
|
|
|
/** |
413
|
|
|
* Get the results full text total warnings grammar formatted. |
414
|
|
|
*/ |
415
|
|
|
protected function getResultsTotalWarningsGrammar(int $totalWarnings): string |
416
|
|
|
{ |
417
|
|
|
return $totalWarnings === 1 |
418
|
|
|
? 'warning' |
419
|
|
|
: 'warnings'; |
420
|
|
|
} |
421
|
|
|
|
422
|
|
|
/** |
423
|
|
|
* Get the results full text total skipped formatted. |
424
|
|
|
*/ |
425
|
|
|
protected function getResultsTotalSkipped(int $totalSkipped): string |
426
|
|
|
{ |
427
|
|
|
if ($totalSkipped <= 0) { |
428
|
|
|
return ''; |
429
|
|
|
} |
430
|
|
|
|
431
|
|
|
return ", {$this->getResultsTotalSkippedCount($totalSkipped)} {$this->getResultsTotalSkippedGrammar($totalSkipped)}, "; |
432
|
|
|
} |
433
|
|
|
|
434
|
|
|
/** |
435
|
|
|
* Get the results full text total skipped count formatted. |
436
|
|
|
*/ |
437
|
|
|
protected function getResultsTotalSkippedCount(int $totalSkipped): string |
438
|
|
|
{ |
439
|
|
|
return (string) $totalSkipped; |
440
|
|
|
} |
441
|
|
|
|
442
|
|
|
/** |
443
|
|
|
* Get the results full text total skipped grammar formatted. |
444
|
|
|
*/ |
445
|
|
|
protected function getResultsTotalSkippedGrammar(int $totalSkipped): string |
|
|
|
|
446
|
|
|
{ |
447
|
|
|
return 'skipped'; |
448
|
|
|
} |
449
|
|
|
|
450
|
|
|
/** |
451
|
|
|
* Get the issues' issue formatted. |
452
|
|
|
*/ |
453
|
|
|
protected function getIssuesIssue(int $num, Test $test, AssertFailureException $error): string |
454
|
|
|
{ |
455
|
|
|
return "{$this->getIssuesIssueNum($num)} {$this->getIssuesIssueName($test)}" |
456
|
|
|
. $this->getIssuesIssueMessage($error) |
457
|
|
|
. $this->getIssuesIssueTrace($error); |
458
|
|
|
} |
459
|
|
|
|
460
|
|
|
/** |
461
|
|
|
* Get the issues' issue num formatted. |
462
|
|
|
*/ |
463
|
|
|
protected function getIssuesIssueNum(int $num): string |
464
|
|
|
{ |
465
|
|
|
return "{$num})"; |
466
|
|
|
} |
467
|
|
|
|
468
|
|
|
/** |
469
|
|
|
* Get the issues' issue num formatted. |
470
|
|
|
*/ |
471
|
|
|
protected function getIssuesIssueName(Test $test): string |
472
|
|
|
{ |
473
|
|
|
return $test->getName(); |
474
|
|
|
} |
475
|
|
|
|
476
|
|
|
/** |
477
|
|
|
* Get the issues' issue message formatted. |
478
|
|
|
*/ |
479
|
|
|
protected function getIssuesIssueMessage(AssertFailureException $error): string |
480
|
|
|
{ |
481
|
|
|
return PHP_EOL . $error->getMessage(); |
482
|
|
|
} |
483
|
|
|
|
484
|
|
|
/** |
485
|
|
|
* Get the issues' issue trace formatted. |
486
|
|
|
*/ |
487
|
|
|
protected function getIssuesIssueTrace(AssertFailureException $error): string |
488
|
|
|
{ |
489
|
|
|
return PHP_EOL . $error->getTraceAsString(); |
490
|
|
|
} |
491
|
|
|
|
492
|
|
|
/** |
493
|
|
|
* Get the issues' blank (no errors) formatted. |
494
|
|
|
*/ |
495
|
|
|
protected function getIssuesBlank(): string |
496
|
|
|
{ |
497
|
|
|
return ''; |
498
|
|
|
} |
499
|
|
|
|
500
|
|
|
/** |
501
|
|
|
* Get the issues' count full formatted. |
502
|
|
|
*/ |
503
|
|
|
protected function getIssuesCountFull(int $count): string |
504
|
|
|
{ |
505
|
|
|
return "There {$this->getIssuesCountGrammar($count)} {$this->getIssuesCount($count)} {$this->getIssuesCountErrorGrammar($count)}:"; |
506
|
|
|
} |
507
|
|
|
|
508
|
|
|
/** |
509
|
|
|
* Get the issues' count formatted. |
510
|
|
|
*/ |
511
|
|
|
protected function getIssuesCount(int $count): string |
512
|
|
|
{ |
513
|
|
|
return (string) $count; |
514
|
|
|
} |
515
|
|
|
|
516
|
|
|
/** |
517
|
|
|
* Get the issues' count grammar formatted. |
518
|
|
|
*/ |
519
|
|
|
protected function getIssuesCountGrammar(int $count): string |
520
|
|
|
{ |
521
|
|
|
return $count === 1 |
522
|
|
|
? 'was' |
523
|
|
|
: 'were'; |
524
|
|
|
} |
525
|
|
|
|
526
|
|
|
/** |
527
|
|
|
* Get the issues' count error grammar formatted. |
528
|
|
|
*/ |
529
|
|
|
protected function getIssuesCountErrorGrammar(int $count): string |
530
|
|
|
{ |
531
|
|
|
return $count === 1 |
532
|
|
|
? 'error' |
533
|
|
|
: 'errors'; |
534
|
|
|
} |
535
|
|
|
|
536
|
|
|
/** |
537
|
|
|
* Get the issues full text formatted. |
538
|
|
|
*/ |
539
|
|
|
protected function getIssues(array $issues): string |
540
|
|
|
{ |
541
|
|
|
return $this->sectionSpacing() |
542
|
|
|
. $this->getIssuesCountFull(count($issues)) |
543
|
|
|
. $this->sectionSpacing() |
544
|
|
|
. implode($this->sectionSpacing(), $issues); |
545
|
|
|
} |
546
|
|
|
} |
547
|
|
|
|
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.