Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php declare(strict_types=1);
2
/*
3
 * This file is part of PHPUnit.
4
 *
5
 * (c) Sebastian Bergmann <sebastian@phpunit.de>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace PHPUnit\Util\TestDox;
11
 
12
use const PHP_EOL;
13
use function array_map;
14
use function get_class;
15
use function implode;
16
use function method_exists;
17
use function preg_split;
18
use function trim;
19
use PHPUnit\Framework\AssertionFailedError;
20
use PHPUnit\Framework\Reorderable;
21
use PHPUnit\Framework\Test;
22
use PHPUnit\Framework\TestCase;
23
use PHPUnit\Framework\TestResult;
24
use PHPUnit\Framework\TestSuite;
25
use PHPUnit\Framework\Warning;
26
use PHPUnit\Runner\BaseTestRunner;
27
use PHPUnit\Runner\PhptTestCase;
28
use PHPUnit\TextUI\DefaultResultPrinter;
29
use Throwable;
30
 
31
/**
32
 * @internal This class is not covered by the backward compatibility promise for PHPUnit
33
 */
34
class TestDoxPrinter extends DefaultResultPrinter
35
{
36
    /**
37
     * @var NamePrettifier
38
     */
39
    protected $prettifier;
40
 
41
    /**
42
     * @var int The number of test results received from the TestRunner
43
     */
44
    protected $testIndex = 0;
45
 
46
    /**
47
     * @var int The number of test results already sent to the output
48
     */
49
    protected $testFlushIndex = 0;
50
 
51
    /**
52
     * @var array<int, array> Buffer for test results
53
     */
54
    protected $testResults = [];
55
 
56
    /**
57
     * @var array<string, int> Lookup table for testname to testResults[index]
58
     */
59
    protected $testNameResultIndex = [];
60
 
61
    /**
62
     * @var bool
63
     */
64
    protected $enableOutputBuffer = false;
65
 
66
    /**
67
     * @var array array<string>
68
     */
69
    protected $originalExecutionOrder = [];
70
 
71
    /**
72
     * @var int
73
     */
74
    protected $spinState = 0;
75
 
76
    /**
77
     * @var bool
78
     */
79
    protected $showProgress = true;
80
 
81
    /**
82
     * @param null|resource|string $out
83
     * @param int|string           $numberOfColumns
84
     *
85
     * @throws \PHPUnit\Framework\Exception
86
     */
87
    public function __construct($out = null, bool $verbose = false, string $colors = self::COLOR_DEFAULT, bool $debug = false, $numberOfColumns = 80, bool $reverse = false)
88
    {
89
        parent::__construct($out, $verbose, $colors, $debug, $numberOfColumns, $reverse);
90
 
91
        $this->prettifier = new NamePrettifier($this->colors);
92
    }
93
 
94
    public function setOriginalExecutionOrder(array $order): void
95
    {
96
        $this->originalExecutionOrder = $order;
97
        $this->enableOutputBuffer     = !empty($order);
98
    }
99
 
100
    public function setShowProgressAnimation(bool $showProgress): void
101
    {
102
        $this->showProgress = $showProgress;
103
    }
104
 
105
    public function printResult(TestResult $result): void
106
    {
107
    }
108
 
109
    /**
110
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
111
     */
112
    public function endTest(Test $test, float $time): void
113
    {
114
        if (!$test instanceof TestCase && !$test instanceof PhptTestCase && !$test instanceof TestSuite) {
115
            return;
116
        }
117
 
118
        if ($this->testHasPassed()) {
119
            $this->registerTestResult($test, null, BaseTestRunner::STATUS_PASSED, $time, false);
120
        }
121
 
122
        if ($test instanceof TestCase || $test instanceof PhptTestCase) {
123
            $this->testIndex++;
124
        }
125
 
126
        parent::endTest($test, $time);
127
    }
128
 
129
    /**
130
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
131
     */
132
    public function addError(Test $test, Throwable $t, float $time): void
133
    {
134
        $this->registerTestResult($test, $t, BaseTestRunner::STATUS_ERROR, $time, true);
135
    }
136
 
137
    /**
138
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
139
     */
140
    public function addWarning(Test $test, Warning $e, float $time): void
141
    {
142
        $this->registerTestResult($test, $e, BaseTestRunner::STATUS_WARNING, $time, true);
143
    }
144
 
145
    /**
146
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
147
     */
148
    public function addFailure(Test $test, AssertionFailedError $e, float $time): void
149
    {
150
        $this->registerTestResult($test, $e, BaseTestRunner::STATUS_FAILURE, $time, true);
151
    }
152
 
153
    /**
154
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
155
     */
156
    public function addIncompleteTest(Test $test, Throwable $t, float $time): void
157
    {
158
        $this->registerTestResult($test, $t, BaseTestRunner::STATUS_INCOMPLETE, $time, false);
159
    }
160
 
161
    /**
162
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
163
     */
164
    public function addRiskyTest(Test $test, Throwable $t, float $time): void
165
    {
166
        $this->registerTestResult($test, $t, BaseTestRunner::STATUS_RISKY, $time, false);
167
    }
168
 
169
    /**
170
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
171
     */
172
    public function addSkippedTest(Test $test, Throwable $t, float $time): void
173
    {
174
        $this->registerTestResult($test, $t, BaseTestRunner::STATUS_SKIPPED, $time, false);
175
    }
176
 
177
    public function writeProgress(string $progress): void
178
    {
179
        $this->flushOutputBuffer();
180
    }
181
 
182
    public function flush(): void
183
    {
184
        $this->flushOutputBuffer(true);
185
    }
186
 
187
    /**
188
     * @throws \SebastianBergmann\RecursionContext\InvalidArgumentException
189
     */
190
    protected function registerTestResult(Test $test, ?Throwable $t, int $status, float $time, bool $verbose): void
191
    {
192
        $testName = $test instanceof Reorderable ? $test->sortId() : $test->getName();
193
 
194
        $result = [
195
            'className'  => $this->formatClassName($test),
196
            'testName'   => $testName,
197
            'testMethod' => $this->formatTestName($test),
198
            'message'    => '',
199
            'status'     => $status,
200
            'time'       => $time,
201
            'verbose'    => $verbose,
202
        ];
203
 
204
        if ($t !== null) {
205
            $result['message'] = $this->formatTestResultMessage($t, $result);
206
        }
207
 
208
        $this->testResults[$this->testIndex]  = $result;
209
        $this->testNameResultIndex[$testName] = $this->testIndex;
210
    }
211
 
212
    protected function formatTestName(Test $test): string
213
    {
214
        return method_exists($test, 'getName') ? $test->getName() : '';
215
    }
216
 
217
    protected function formatClassName(Test $test): string
218
    {
219
        return get_class($test);
220
    }
221
 
222
    protected function testHasPassed(): bool
223
    {
224
        if (!isset($this->testResults[$this->testIndex]['status'])) {
225
            return true;
226
        }
227
 
228
        if ($this->testResults[$this->testIndex]['status'] === BaseTestRunner::STATUS_PASSED) {
229
            return true;
230
        }
231
 
232
        return false;
233
    }
234
 
235
    protected function flushOutputBuffer(bool $forceFlush = false): void
236
    {
237
        if ($this->testFlushIndex === $this->testIndex) {
238
            return;
239
        }
240
 
241
        if ($this->testFlushIndex > 0) {
242
            if ($this->enableOutputBuffer &&
243
                isset($this->originalExecutionOrder[$this->testFlushIndex - 1])) {
244
                $prevResult = $this->getTestResultByName($this->originalExecutionOrder[$this->testFlushIndex - 1]);
245
            } else {
246
                $prevResult = $this->testResults[$this->testFlushIndex - 1];
247
            }
248
        } else {
249
            $prevResult = $this->getEmptyTestResult();
250
        }
251
 
252
        if (!$this->enableOutputBuffer) {
253
            $this->writeTestResult($prevResult, $this->testResults[$this->testFlushIndex++]);
254
        } else {
255
            do {
256
                $flushed = false;
257
 
258
                if (!$forceFlush && isset($this->originalExecutionOrder[$this->testFlushIndex])) {
259
                    $result = $this->getTestResultByName($this->originalExecutionOrder[$this->testFlushIndex]);
260
                } else {
261
                    // This test(name) cannot found in original execution order,
262
                    // flush result to output stream right away
263
                    $result = $this->testResults[$this->testFlushIndex];
264
                }
265
 
266
                if (!empty($result)) {
267
                    $this->hideSpinner();
268
                    $this->writeTestResult($prevResult, $result);
269
                    $this->testFlushIndex++;
270
                    $prevResult = $result;
271
                    $flushed    = true;
272
                } else {
273
                    $this->showSpinner();
274
                }
275
            } while ($flushed && $this->testFlushIndex < $this->testIndex);
276
        }
277
    }
278
 
279
    protected function showSpinner(): void
280
    {
281
        if (!$this->showProgress) {
282
            return;
283
        }
284
 
285
        if ($this->spinState) {
286
            $this->undrawSpinner();
287
        }
288
 
289
        $this->spinState++;
290
        $this->drawSpinner();
291
    }
292
 
293
    protected function hideSpinner(): void
294
    {
295
        if (!$this->showProgress) {
296
            return;
297
        }
298
 
299
        if ($this->spinState) {
300
            $this->undrawSpinner();
301
        }
302
 
303
        $this->spinState = 0;
304
    }
305
 
306
    protected function drawSpinner(): void
307
    {
308
        // optional for CLI printers: show the user a 'buffering output' spinner
309
    }
310
 
311
    protected function undrawSpinner(): void
312
    {
313
        // remove the spinner from the current line
314
    }
315
 
316
    protected function writeTestResult(array $prevResult, array $result): void
317
    {
318
    }
319
 
320
    protected function getEmptyTestResult(): array
321
    {
322
        return [
323
            'className' => '',
324
            'testName'  => '',
325
            'message'   => '',
326
            'failed'    => '',
327
            'verbose'   => '',
328
        ];
329
    }
330
 
331
    protected function getTestResultByName(?string $testName): array
332
    {
333
        if (isset($this->testNameResultIndex[$testName])) {
334
            return $this->testResults[$this->testNameResultIndex[$testName]];
335
        }
336
 
337
        return [];
338
    }
339
 
340
    protected function formatThrowable(Throwable $t, ?int $status = null): string
341
    {
342
        $message = trim(\PHPUnit\Framework\TestFailure::exceptionToString($t));
343
 
344
        if ($message) {
345
            $message .= PHP_EOL . PHP_EOL . $this->formatStacktrace($t);
346
        } else {
347
            $message = $this->formatStacktrace($t);
348
        }
349
 
350
        return $message;
351
    }
352
 
353
    protected function formatStacktrace(Throwable $t): string
354
    {
355
        return \PHPUnit\Util\Filter::getFilteredStacktrace($t);
356
    }
357
 
358
    protected function formatTestResultMessage(Throwable $t, array $result, string $prefix = '│'): string
359
    {
360
        $message = $this->formatThrowable($t, $result['status']);
361
 
362
        if ($message === '') {
363
            return '';
364
        }
365
 
366
        if (!($this->verbose || $result['verbose'])) {
367
            return '';
368
        }
369
 
370
        return $this->prefixLines($prefix, $message);
371
    }
372
 
373
    protected function prefixLines(string $prefix, string $message): string
374
    {
375
        $message = trim($message);
376
 
377
        return implode(
378
            PHP_EOL,
379
            array_map(
380
                static function (string $text) use ($prefix)
381
                {
382
                    return '   ' . $prefix . ($text ? ' ' . $text : '');
383
                },
384
                preg_split('/\r\n|\r|\n/', $message)
385
            )
386
        );
387
    }
388
}