Total Complexity | 72 |
Total Lines | 513 |
Duplicated Lines | 0 % |
Changes | 1 | ||
Bugs | 0 | Features | 0 |
Complex classes like Formatter often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Formatter, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
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 |
||
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.