Subversion-Projekte lars-tiefland.laravel_shop

Revision

Zur aktuellen 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\TextUI;
11
 
12
use const PHP_EOL;
13
use function array_map;
14
use function array_reverse;
15
use function count;
16
use function floor;
17
use function implode;
18
use function in_array;
19
use function is_int;
20
use function max;
21
use function preg_split;
22
use function sprintf;
23
use function str_pad;
24
use function str_repeat;
25
use function strlen;
26
use function trim;
27
use function vsprintf;
28
use PHPUnit\Framework\AssertionFailedError;
29
use PHPUnit\Framework\Exception;
30
use PHPUnit\Framework\InvalidArgumentException;
31
use PHPUnit\Framework\Test;
32
use PHPUnit\Framework\TestCase;
33
use PHPUnit\Framework\TestFailure;
34
use PHPUnit\Framework\TestResult;
35
use PHPUnit\Framework\TestSuite;
36
use PHPUnit\Framework\Warning;
37
use PHPUnit\Runner\PhptTestCase;
38
use PHPUnit\Util\Color;
39
use PHPUnit\Util\Printer;
40
use SebastianBergmann\Environment\Console;
41
use SebastianBergmann\Timer\ResourceUsageFormatter;
42
use SebastianBergmann\Timer\Timer;
43
use Throwable;
44
 
45
/**
46
 * @internal This class is not covered by the backward compatibility promise for PHPUnit
47
 */
48
class DefaultResultPrinter extends Printer implements ResultPrinter
49
{
50
    public const EVENT_TEST_START = 0;
51
 
52
    public const EVENT_TEST_END = 1;
53
 
54
    public const EVENT_TESTSUITE_START = 2;
55
 
56
    public const EVENT_TESTSUITE_END = 3;
57
 
58
    public const COLOR_NEVER = 'never';
59
 
60
    public const COLOR_AUTO = 'auto';
61
 
62
    public const COLOR_ALWAYS = 'always';
63
 
64
    public const COLOR_DEFAULT = self::COLOR_NEVER;
65
 
66
    private const AVAILABLE_COLORS = [self::COLOR_NEVER, self::COLOR_AUTO, self::COLOR_ALWAYS];
67
 
68
    /**
69
     * @var int
70
     */
71
    protected $column = 0;
72
 
73
    /**
74
     * @var int
75
     */
76
    protected $maxColumn;
77
 
78
    /**
79
     * @var bool
80
     */
81
    protected $lastTestFailed = false;
82
 
83
    /**
84
     * @var int
85
     */
86
    protected $numAssertions = 0;
87
 
88
    /**
89
     * @var int
90
     */
91
    protected $numTests = -1;
92
 
93
    /**
94
     * @var int
95
     */
96
    protected $numTestsRun = 0;
97
 
98
    /**
99
     * @var int
100
     */
101
    protected $numTestsWidth;
102
 
103
    /**
104
     * @var bool
105
     */
106
    protected $colors = false;
107
 
108
    /**
109
     * @var bool
110
     */
111
    protected $debug = false;
112
 
113
    /**
114
     * @var bool
115
     */
116
    protected $verbose = false;
117
 
118
    /**
119
     * @var int
120
     */
121
    private $numberOfColumns;
122
 
123
    /**
124
     * @var bool
125
     */
126
    private $reverse;
127
 
128
    /**
129
     * @var bool
130
     */
131
    private $defectListPrinted = false;
132
 
133
    /**
134
     * @var Timer
135
     */
136
    private $timer;
137
 
138
    /**
139
     * Constructor.
140
     *
141
     * @param null|resource|string $out
142
     * @param int|string           $numberOfColumns
143
     *
144
     * @throws Exception
145
     */
146
    public function __construct($out = null, bool $verbose = false, string $colors = self::COLOR_DEFAULT, bool $debug = false, $numberOfColumns = 80, bool $reverse = false)
147
    {
148
        parent::__construct($out);
149
 
150
        if (!in_array($colors, self::AVAILABLE_COLORS, true)) {
151
            throw InvalidArgumentException::create(
152
                3,
153
                vsprintf('value from "%s", "%s" or "%s"', self::AVAILABLE_COLORS)
154
            );
155
        }
156
 
157
        if (!is_int($numberOfColumns) && $numberOfColumns !== 'max') {
158
            throw InvalidArgumentException::create(5, 'integer or "max"');
159
        }
160
 
161
        $console            = new Console;
162
        $maxNumberOfColumns = $console->getNumberOfColumns();
163
 
164
        if ($numberOfColumns === 'max' || ($numberOfColumns !== 80 && $numberOfColumns > $maxNumberOfColumns)) {
165
            $numberOfColumns = $maxNumberOfColumns;
166
        }
167
 
168
        $this->numberOfColumns = $numberOfColumns;
169
        $this->verbose         = $verbose;
170
        $this->debug           = $debug;
171
        $this->reverse         = $reverse;
172
 
173
        if ($colors === self::COLOR_AUTO && $console->hasColorSupport()) {
174
            $this->colors = true;
175
        } else {
176
            $this->colors = (self::COLOR_ALWAYS === $colors);
177
        }
178
 
179
        $this->timer = new Timer;
180
 
181
        $this->timer->start();
182
    }
183
 
184
    public function printResult(TestResult $result): void
185
    {
186
        $this->printHeader($result);
187
        $this->printErrors($result);
188
        $this->printWarnings($result);
189
        $this->printFailures($result);
190
        $this->printRisky($result);
191
 
192
        if ($this->verbose) {
193
            $this->printIncompletes($result);
194
            $this->printSkipped($result);
195
        }
196
 
197
        $this->printFooter($result);
198
    }
199
 
200
    /**
201
     * An error occurred.
202
     */
203
    public function addError(Test $test, Throwable $t, float $time): void
204
    {
205
        $this->writeProgressWithColor('fg-red, bold', 'E');
206
        $this->lastTestFailed = true;
207
    }
208
 
209
    /**
210
     * A failure occurred.
211
     */
212
    public function addFailure(Test $test, AssertionFailedError $e, float $time): void
213
    {
214
        $this->writeProgressWithColor('bg-red, fg-white', 'F');
215
        $this->lastTestFailed = true;
216
    }
217
 
218
    /**
219
     * A warning occurred.
220
     */
221
    public function addWarning(Test $test, Warning $e, float $time): void
222
    {
223
        $this->writeProgressWithColor('fg-yellow, bold', 'W');
224
        $this->lastTestFailed = true;
225
    }
226
 
227
    /**
228
     * Incomplete test.
229
     */
230
    public function addIncompleteTest(Test $test, Throwable $t, float $time): void
231
    {
232
        $this->writeProgressWithColor('fg-yellow, bold', 'I');
233
        $this->lastTestFailed = true;
234
    }
235
 
236
    /**
237
     * Risky test.
238
     */
239
    public function addRiskyTest(Test $test, Throwable $t, float $time): void
240
    {
241
        $this->writeProgressWithColor('fg-yellow, bold', 'R');
242
        $this->lastTestFailed = true;
243
    }
244
 
245
    /**
246
     * Skipped test.
247
     */
248
    public function addSkippedTest(Test $test, Throwable $t, float $time): void
249
    {
250
        $this->writeProgressWithColor('fg-cyan, bold', 'S');
251
        $this->lastTestFailed = true;
252
    }
253
 
254
    /**
255
     * A testsuite started.
256
     */
257
    public function startTestSuite(TestSuite $suite): void
258
    {
259
        if ($this->numTests == -1) {
260
            $this->numTests      = count($suite);
261
            $this->numTestsWidth = strlen((string) $this->numTests);
262
            $this->maxColumn     = $this->numberOfColumns - strlen('  /  (XXX%)') - (2 * $this->numTestsWidth);
263
        }
264
    }
265
 
266
    /**
267
     * A testsuite ended.
268
     */
269
    public function endTestSuite(TestSuite $suite): void
270
    {
271
    }
272
 
273
    /**
274
     * A test started.
275
     */
276
    public function startTest(Test $test): void
277
    {
278
        if ($this->debug) {
279
            $this->write(
280
                sprintf(
281
                    "Test '%s' started\n",
282
                    \PHPUnit\Util\Test::describeAsString($test)
283
                )
284
            );
285
        }
286
    }
287
 
288
    /**
289
     * A test ended.
290
     */
291
    public function endTest(Test $test, float $time): void
292
    {
293
        if ($this->debug) {
294
            $this->write(
295
                sprintf(
296
                    "Test '%s' ended\n",
297
                    \PHPUnit\Util\Test::describeAsString($test)
298
                )
299
            );
300
        }
301
 
302
        if (!$this->lastTestFailed) {
303
            $this->writeProgress('.');
304
        }
305
 
306
        if ($test instanceof TestCase) {
307
            $this->numAssertions += $test->getNumAssertions();
308
        } elseif ($test instanceof PhptTestCase) {
309
            $this->numAssertions++;
310
        }
311
 
312
        $this->lastTestFailed = false;
313
 
314
        if ($test instanceof TestCase && !$test->hasExpectationOnOutput()) {
315
            $this->write($test->getActualOutput());
316
        }
317
    }
318
 
319
    protected function printDefects(array $defects, string $type): void
320
    {
321
        $count = count($defects);
322
 
323
        if ($count == 0) {
324
            return;
325
        }
326
 
327
        if ($this->defectListPrinted) {
328
            $this->write("\n--\n\n");
329
        }
330
 
331
        $this->write(
332
            sprintf(
333
                "There %s %d %s%s:\n",
334
                ($count == 1) ? 'was' : 'were',
335
                $count,
336
                $type,
337
                ($count == 1) ? '' : 's'
338
            )
339
        );
340
 
341
        $i = 1;
342
 
343
        if ($this->reverse) {
344
            $defects = array_reverse($defects);
345
        }
346
 
347
        foreach ($defects as $defect) {
348
            $this->printDefect($defect, $i++);
349
        }
350
 
351
        $this->defectListPrinted = true;
352
    }
353
 
354
    protected function printDefect(TestFailure $defect, int $count): void
355
    {
356
        $this->printDefectHeader($defect, $count);
357
        $this->printDefectTrace($defect);
358
    }
359
 
360
    protected function printDefectHeader(TestFailure $defect, int $count): void
361
    {
362
        $this->write(
363
            sprintf(
364
                "\n%d) %s\n",
365
                $count,
366
                $defect->getTestName()
367
            )
368
        );
369
    }
370
 
371
    protected function printDefectTrace(TestFailure $defect): void
372
    {
373
        $e = $defect->thrownException();
374
 
375
        $this->write((string) $e);
376
 
377
        while ($e = $e->getPrevious()) {
378
            $this->write("\nCaused by\n" . trim((string) $e) . "\n");
379
        }
380
    }
381
 
382
    protected function printErrors(TestResult $result): void
383
    {
384
        $this->printDefects($result->errors(), 'error');
385
    }
386
 
387
    protected function printFailures(TestResult $result): void
388
    {
389
        $this->printDefects($result->failures(), 'failure');
390
    }
391
 
392
    protected function printWarnings(TestResult $result): void
393
    {
394
        $this->printDefects($result->warnings(), 'warning');
395
    }
396
 
397
    protected function printIncompletes(TestResult $result): void
398
    {
399
        $this->printDefects($result->notImplemented(), 'incomplete test');
400
    }
401
 
402
    protected function printRisky(TestResult $result): void
403
    {
404
        $this->printDefects($result->risky(), 'risky test');
405
    }
406
 
407
    protected function printSkipped(TestResult $result): void
408
    {
409
        $this->printDefects($result->skipped(), 'skipped test');
410
    }
411
 
412
    protected function printHeader(TestResult $result): void
413
    {
414
        if (count($result) > 0) {
415
            $this->write(PHP_EOL . PHP_EOL . (new ResourceUsageFormatter)->resourceUsage($this->timer->stop()) . PHP_EOL . PHP_EOL);
416
        }
417
    }
418
 
419
    protected function printFooter(TestResult $result): void
420
    {
421
        if (count($result) === 0) {
422
            $this->writeWithColor(
423
                'fg-black, bg-yellow',
424
                'No tests executed!'
425
            );
426
 
427
            return;
428
        }
429
 
430
        if ($result->wasSuccessfulAndNoTestIsRiskyOrSkippedOrIncomplete()) {
431
            $this->writeWithColor(
432
                'fg-black, bg-green',
433
                sprintf(
434
                    'OK (%d test%s, %d assertion%s)',
435
                    count($result),
436
                    (count($result) === 1) ? '' : 's',
437
                    $this->numAssertions,
438
                    ($this->numAssertions === 1) ? '' : 's'
439
                )
440
            );
441
 
442
            return;
443
        }
444
 
445
        $color = 'fg-black, bg-yellow';
446
 
447
        if ($result->wasSuccessful()) {
448
            if ($this->verbose || !$result->allHarmless()) {
449
                $this->write("\n");
450
            }
451
 
452
            $this->writeWithColor(
453
                $color,
454
                'OK, but incomplete, skipped, or risky tests!'
455
            );
456
        } else {
457
            $this->write("\n");
458
 
459
            if ($result->errorCount()) {
460
                $color = 'fg-white, bg-red';
461
 
462
                $this->writeWithColor(
463
                    $color,
464
                    'ERRORS!'
465
                );
466
            } elseif ($result->failureCount()) {
467
                $color = 'fg-white, bg-red';
468
 
469
                $this->writeWithColor(
470
                    $color,
471
                    'FAILURES!'
472
                );
473
            } elseif ($result->warningCount()) {
474
                $color = 'fg-black, bg-yellow';
475
 
476
                $this->writeWithColor(
477
                    $color,
478
                    'WARNINGS!'
479
                );
480
            }
481
        }
482
 
483
        $this->writeCountString(count($result), 'Tests', $color, true);
484
        $this->writeCountString($this->numAssertions, 'Assertions', $color, true);
485
        $this->writeCountString($result->errorCount(), 'Errors', $color);
486
        $this->writeCountString($result->failureCount(), 'Failures', $color);
487
        $this->writeCountString($result->warningCount(), 'Warnings', $color);
488
        $this->writeCountString($result->skippedCount(), 'Skipped', $color);
489
        $this->writeCountString($result->notImplementedCount(), 'Incomplete', $color);
490
        $this->writeCountString($result->riskyCount(), 'Risky', $color);
491
        $this->writeWithColor($color, '.');
492
    }
493
 
494
    protected function writeProgress(string $progress): void
495
    {
496
        if ($this->debug) {
497
            return;
498
        }
499
 
500
        $this->write($progress);
501
        $this->column++;
502
        $this->numTestsRun++;
503
 
504
        if ($this->column == $this->maxColumn || $this->numTestsRun == $this->numTests) {
505
            if ($this->numTestsRun == $this->numTests) {
506
                $this->write(str_repeat(' ', $this->maxColumn - $this->column));
507
            }
508
 
509
            $this->write(
510
                sprintf(
511
                    ' %' . $this->numTestsWidth . 'd / %' .
512
                    $this->numTestsWidth . 'd (%3s%%)',
513
                    $this->numTestsRun,
514
                    $this->numTests,
515
                    floor(($this->numTestsRun / $this->numTests) * 100)
516
                )
517
            );
518
 
519
            if ($this->column == $this->maxColumn) {
520
                $this->writeNewLine();
521
            }
522
        }
523
    }
524
 
525
    protected function writeNewLine(): void
526
    {
527
        $this->column = 0;
528
        $this->write("\n");
529
    }
530
 
531
    /**
532
     * Formats a buffer with a specified ANSI color sequence if colors are
533
     * enabled.
534
     */
535
    protected function colorizeTextBox(string $color, string $buffer): string
536
    {
537
        if (!$this->colors) {
538
            return $buffer;
539
        }
540
 
541
        $lines   = preg_split('/\r\n|\r|\n/', $buffer);
542
        $padding = max(array_map('\strlen', $lines));
543
 
544
        $styledLines = [];
545
 
546
        foreach ($lines as $line) {
547
            $styledLines[] = Color::colorize($color, str_pad($line, $padding));
548
        }
549
 
550
        return implode(PHP_EOL, $styledLines);
551
    }
552
 
553
    /**
554
     * Writes a buffer out with a color sequence if colors are enabled.
555
     */
556
    protected function writeWithColor(string $color, string $buffer, bool $lf = true): void
557
    {
558
        $this->write($this->colorizeTextBox($color, $buffer));
559
 
560
        if ($lf) {
561
            $this->write(PHP_EOL);
562
        }
563
    }
564
 
565
    /**
566
     * Writes progress with a color sequence if colors are enabled.
567
     */
568
    protected function writeProgressWithColor(string $color, string $buffer): void
569
    {
570
        $buffer = $this->colorizeTextBox($color, $buffer);
571
        $this->writeProgress($buffer);
572
    }
573
 
574
    private function writeCountString(int $count, string $name, string $color, bool $always = false): void
575
    {
576
        static $first = true;
577
 
578
        if ($always || $count > 0) {
579
            $this->writeWithColor(
580
                $color,
581
                sprintf(
582
                    '%s%s: %d',
583
                    !$first ? ', ' : '',
584
                    $name,
585
                    $count
586
                ),
587
                false
588
            );
589
 
590
            $first = false;
591
        }
592
    }
593
}