Passed
Push — v0.2 ( e01ff2...ca53bb )
by Freddie
02:09
created

TestResultPrinter::getTestClass()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php declare(strict_types = 1);
2
3
namespace Simplex\Quickstart\Shared\Testing;
4
5
use PHPUnit\Framework\AssertionFailedError;
6
use PHPUnit\Framework\ExpectationFailedException;
7
use PHPUnit\Framework\SelfDescribing;
8
use PHPUnit\Framework\Test;
9
use PHPUnit\Framework\TestCase;
10
use PHPUnit\Framework\TestFailure;
11
use PHPUnit\Framework\Warning;
12
use PHPUnit\Runner\PhptTestCase;
13
use PHPUnit\TextUI\ResultPrinter as PhpUnitResultPrinter;
14
use SebastianBergmann\Comparator\ComparisonFailure;
15
16
/**
17
 * @SuppressWarnings(PHPMD)
18
 */
19
class TestResultPrinter extends PhpUnitResultPrinter
20
{
21
    private const INDENT = '      ';
22
23
    private const LINE_FEED = "\n";
24
25
    private const RED = 'fg-red';
26
    private const WHITE = 'fg-white';
27
    private const WHITE_BOLD = 'fg-white, bold';
28
    private const GREEN = 'fg-green';
29
    private const CYAN_BOLD = 'fg-cyan, bold';
30
31
    private const GREEN_BLOCK = 'fgg-green, bg-green';
32
    private const RED_BLOCK = 'fgg-red, bg-red';
33
34
    private const CROSS_SYMBOL = '✘';
35
    private const TICK_SYMBOL = '✓';
36
37
    public function addError(Test $test, \Exception $e, $time)
38
    {
39
        $this->printFailureInfoLine($test, $time);
40
41
        $this->lastTestFailed = true;
42
    }
43
44
    private function printFailureInfoLine(Test $test, $time): void
45
    {
46
        $this->write(self::INDENT);
47
        $this->writeWithColor(self::RED, self::CROSS_SYMBOL . ' ', false);
48
        $this->printTestDescription($test, $time);
49
    }
50
51
    private function printTestDescription(Test $test, $time)
52
    {
53
        $this->writeWithColor(self::CYAN_BOLD, $this->getTestClass($test), false);
54
55
        if (!$this->isTestDescribable($test)) {
56
            $this->write(self::LINE_FEED);
57
            return;
58
        }
59
60
        $this->writeWithColor(self::CYAN_BOLD, ': ', false);
61
        $this->writeWithColor(self::WHITE, $this->getTestDescription($test), false);
62
        $this->writeWithColor(self::WHITE_BOLD, $this->getTimeAsString($time));
63
    }
64
65
    private function getTestClass(Test $test): string
66
    {
67
        return get_class($test);
68
    }
69
70
    private function isTestDescribable(Test $test): bool
71
    {
72
        return $test instanceof TestCase || $test instanceof  SelfDescribing;
73
    }
74
75
    private function getTestDescription(Test $test)
76
    {
77
        if ($test instanceof TestCase) {
78
            return str_replace('_', ' ', $test->getName());
79
        }
80
81
        if ($test instanceof SelfDescribing) {
82
            return $test->toString();
83
        }
84
85
        throw new \LogicException(get_class($test) . ' is not describable');
86
    }
87
88
    private function getTimeAsString(float $time): string
89
    {
90
        $ms = round($time * 1000);
91
92
        return ' ' . (string) $ms . 'ms';
93
    }
94
95
    public function addFailure(Test $test, AssertionFailedError $e, $time)
96
    {
97
        $this->printFailureInfoLine($test, $time);
98
99
        $this->lastTestFailed = true;
100
    }
101
102
    public function addWarning(Test $test, Warning $e, $time)
103
    {
104
        $this->lastTestFailed = true;
105
    }
106
107
    public function addIncompleteTest(Test $test, \Exception $e, $time)
108
    {
109
        $this->lastTestFailed = true;
110
    }
111
112
    public function addRiskyTest(Test $test, \Exception $e, $time)
113
    {
114
        $this->lastTestFailed = true;
115
    }
116
117
    public function addSkippedTest(Test $test, \Exception $e, $time)
118
    {
119
        $this->lastTestFailed = true;
120
    }
121
122
    public function endTest(Test $test, $time)
123
    {
124
        if (!$this->lastTestFailed) {
125
            $this->printSuccessLineInfo($test, $time);
126
        }
127
128
        $this->incrementAssertionCount($test);
129
130
        $this->lastTestFailed = false;
131
132
        if (!$test instanceof TestCase) {
133
            return;
134
        }
135
136
        if ($test->hasExpectationOnOutput()) {
137
            return;
138
        }
139
140
        $this->write($test->getActualOutput());
141
    }
142
143
    private function printSuccessLineInfo(Test $test, $time): void
144
    {
145
        $this->write(self::INDENT);
146
        $this->writeWithColor(self::GREEN, self::TICK_SYMBOL . ' ', false);
147
        $this->printTestDescription($test, $time);
148
    }
149
150
    private function incrementAssertionCount(Test $test): void
151
    {
152
        if ($test instanceof TestCase) {
153
            $this->numAssertions += $test->getNumAssertions();
154
        } elseif ($test instanceof PhptTestCase) {
155
            ++$this->numAssertions;
156
        }
157
    }
158
159
    protected function printDefectTrace(TestFailure $defect)
160
    {
161
        $exception = $defect->thrownException();
162
163
        $this->printExceptions($exception);
164
165
        if (!$exception instanceof ExpectationFailedException) {
166
            $this->printGotoTip($exception);
167
            return;
168
        }
169
170
        $comparisonFailure = $exception->getComparisonFailure();
171
172
        if (null === $comparisonFailure) {
173
            return;
174
        }
175
176
        $this->printComparison($comparisonFailure);
177
    }
178
179
    private function printExceptions(\Throwable $exception): void
180
    {
181
        $this->printException($exception);
182
183
        while ($previous = $exception->getPrevious()) {
184
            $this->write(self::LINE_FEED . 'Caused by:' . self::LINE_FEED);
185
            $this->printException($previous);
186
        }
187
    }
188
189
    private function printException(\Throwable $exception): void
190
    {
191
        $this->write(self::LINE_FEED);
192
193
        $lines = explode(self::LINE_FEED, (string) $exception);
194
195
        foreach ($lines as $line) {
196
            $this->write(self::INDENT);
197
            $this->writeWithColor(self::RED_BLOCK, ' ', false);
198
            $this->write('  ');
199
            $this->writeWithColor(self::WHITE, $line);
200
        }
201
    }
202
203
    protected function printGotoTip(\Throwable $exception): void
204
    {
205
        $file = new \SplFileInfo($exception->getFile());
206
207
        $this->write(self::INDENT);
208
        $this->writeWithColor(self::RED_BLOCK, ' ', false);
209
        $this->writeWithColor(self::WHITE_BOLD, '  Goto: ' . $file->getBasename() . ':' . $exception->getLine());
210
    }
211
212
    private function printComparison(ComparisonFailure $failure): void
213
    {
214
        $this->writeWithColor(self::CYAN_BOLD, self::INDENT . "Expected:");
215
216
        $expected = $this->getExpectedAsString($failure);
217
        $this->printComparisonDetail($expected, self::GREEN_BLOCK);
218
219
        $this->writeWithColor(self::CYAN_BOLD, self::INDENT . "Actual");
220
221
        $actual = $this->getActualAsString($failure);
222
        $this->printComparisonDetail($actual, self::RED_BLOCK);
223
    }
224
225
    private function printComparisonDetail(string $detail, string $blockColor): void
226
    {
227
        $this->write(self::LINE_FEED);
228
229
        $lines = explode(self::LINE_FEED, $detail);
230
231
        foreach ($lines as $line) {
232
            $this->write(self::INDENT);
233
            $this->writeWithColor($blockColor, ' ', false);
234
            $this->write('  ');
235
            $this->writeWithColor(self::WHITE_BOLD, $line);
236
        }
237
    }
238
239
    private function getExpectedAsString(ComparisonFailure $failure): string
240
    {
241
        $expected = $failure->getExpectedAsString();
242
243
        if (empty($expected)) {
244
            $expected = (string) $failure->getExpected();
245
        }
246
247
        return $expected;
248
    }
249
250
    private function getActualAsString(ComparisonFailure $failure): string
251
    {
252
        $actual = $failure->getActualAsString();
253
254
        if (empty($actual)) {
255
            $actual = (string) $failure->getActual();
256
        }
257
258
        return $actual;
259
    }
260
}
261