Subversion-Projekte lars-tiefland.laravel_shop

Revision

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