TestResultPrinter::addSkippedTest()   A
last analyzed

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