Subversion-Projekte lars-tiefland.laravel_shop

Revision

Revision 150 | Zur aktuellen Revision | 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 const PHP_SAPI;
14
use const PHP_VERSION;
15
use function array_diff;
16
use function array_map;
17
use function array_merge;
18
use function assert;
19
use function class_exists;
20
use function count;
21
use function dirname;
22
use function file_put_contents;
23
use function htmlspecialchars;
24
use function is_array;
25
use function is_int;
26
use function is_string;
27
use function mt_srand;
28
use function range;
29
use function realpath;
150 lars 30
use function sort;
148 lars 31
use function sprintf;
32
use function time;
33
use PHPUnit\Framework\Exception;
34
use PHPUnit\Framework\TestResult;
35
use PHPUnit\Framework\TestSuite;
36
use PHPUnit\Runner\AfterLastTestHook;
37
use PHPUnit\Runner\BaseTestRunner;
38
use PHPUnit\Runner\BeforeFirstTestHook;
39
use PHPUnit\Runner\DefaultTestResultCache;
40
use PHPUnit\Runner\Extension\ExtensionHandler;
41
use PHPUnit\Runner\Filter\ExcludeGroupFilterIterator;
42
use PHPUnit\Runner\Filter\Factory;
43
use PHPUnit\Runner\Filter\IncludeGroupFilterIterator;
44
use PHPUnit\Runner\Filter\NameFilterIterator;
45
use PHPUnit\Runner\Hook;
46
use PHPUnit\Runner\NullTestResultCache;
47
use PHPUnit\Runner\ResultCacheExtension;
48
use PHPUnit\Runner\StandardTestSuiteLoader;
49
use PHPUnit\Runner\TestHook;
50
use PHPUnit\Runner\TestListenerAdapter;
51
use PHPUnit\Runner\TestSuiteLoader;
52
use PHPUnit\Runner\TestSuiteSorter;
53
use PHPUnit\Runner\Version;
54
use PHPUnit\TextUI\XmlConfiguration\CodeCoverage\FilterMapper;
55
use PHPUnit\TextUI\XmlConfiguration\Configuration;
56
use PHPUnit\TextUI\XmlConfiguration\Loader;
57
use PHPUnit\TextUI\XmlConfiguration\PhpHandler;
58
use PHPUnit\Util\Filesystem;
59
use PHPUnit\Util\Log\JUnit;
60
use PHPUnit\Util\Log\TeamCity;
61
use PHPUnit\Util\Printer;
62
use PHPUnit\Util\TestDox\CliTestDoxPrinter;
63
use PHPUnit\Util\TestDox\HtmlResultPrinter;
64
use PHPUnit\Util\TestDox\TextResultPrinter;
65
use PHPUnit\Util\TestDox\XmlResultPrinter;
66
use PHPUnit\Util\XdebugFilterScriptGenerator;
67
use PHPUnit\Util\Xml\SchemaDetector;
68
use ReflectionClass;
69
use ReflectionException;
70
use SebastianBergmann\CodeCoverage\CodeCoverage;
71
use SebastianBergmann\CodeCoverage\Driver\Selector;
72
use SebastianBergmann\CodeCoverage\Exception as CodeCoverageException;
73
use SebastianBergmann\CodeCoverage\Filter as CodeCoverageFilter;
74
use SebastianBergmann\CodeCoverage\Report\Clover as CloverReport;
75
use SebastianBergmann\CodeCoverage\Report\Cobertura as CoberturaReport;
76
use SebastianBergmann\CodeCoverage\Report\Crap4j as Crap4jReport;
77
use SebastianBergmann\CodeCoverage\Report\Html\Facade as HtmlReport;
78
use SebastianBergmann\CodeCoverage\Report\PHP as PhpReport;
79
use SebastianBergmann\CodeCoverage\Report\Text as TextReport;
80
use SebastianBergmann\CodeCoverage\Report\Xml\Facade as XmlReport;
81
use SebastianBergmann\Comparator\Comparator;
82
use SebastianBergmann\Environment\Runtime;
83
use SebastianBergmann\Invoker\Invoker;
84
use SebastianBergmann\Timer\Timer;
85
 
86
/**
87
 * @internal This class is not covered by the backward compatibility promise for PHPUnit
88
 */
89
final class TestRunner extends BaseTestRunner
90
{
91
    public const SUCCESS_EXIT = 0;
92
 
93
    public const FAILURE_EXIT = 1;
94
 
95
    public const EXCEPTION_EXIT = 2;
96
 
97
    /**
98
     * @var CodeCoverageFilter
99
     */
100
    private $codeCoverageFilter;
101
 
102
    /**
103
     * @var TestSuiteLoader
104
     */
105
    private $loader;
106
 
107
    /**
108
     * @var ResultPrinter
109
     */
110
    private $printer;
111
 
112
    /**
113
     * @var bool
114
     */
115
    private $messagePrinted = false;
116
 
117
    /**
118
     * @var Hook[]
119
     */
120
    private $extensions = [];
121
 
122
    /**
123
     * @var Timer
124
     */
125
    private $timer;
126
 
127
    public function __construct(TestSuiteLoader $loader = null, CodeCoverageFilter $filter = null)
128
    {
129
        if ($filter === null) {
130
            $filter = new CodeCoverageFilter;
131
        }
132
 
133
        $this->codeCoverageFilter = $filter;
134
        $this->loader             = $loader;
135
        $this->timer              = new Timer;
136
    }
137
 
138
    /**
139
     * @throws \PHPUnit\Runner\Exception
140
     * @throws \PHPUnit\TextUI\XmlConfiguration\Exception
141
     * @throws Exception
142
     */
143
    public function run(TestSuite $suite, array $arguments = [], array $warnings = [], bool $exit = true): TestResult
144
    {
145
        if (isset($arguments['configuration'])) {
146
            $GLOBALS['__PHPUNIT_CONFIGURATION_FILE'] = $arguments['configuration'];
147
        }
148
 
149
        $this->handleConfiguration($arguments);
150
 
151
        $warnings = array_merge($warnings, $arguments['warnings']);
152
 
153
        if (is_int($arguments['columns']) && $arguments['columns'] < 16) {
154
            $arguments['columns']   = 16;
155
            $tooFewColumnsRequested = true;
156
        }
157
 
158
        if (isset($arguments['bootstrap'])) {
159
            $GLOBALS['__PHPUNIT_BOOTSTRAP'] = $arguments['bootstrap'];
160
        }
161
 
162
        if ($arguments['backupGlobals'] === true) {
163
            $suite->setBackupGlobals(true);
164
        }
165
 
166
        if ($arguments['backupStaticAttributes'] === true) {
167
            $suite->setBackupStaticAttributes(true);
168
        }
169
 
170
        if ($arguments['beStrictAboutChangesToGlobalState'] === true) {
171
            $suite->setBeStrictAboutChangesToGlobalState(true);
172
        }
173
 
174
        if ($arguments['executionOrder'] === TestSuiteSorter::ORDER_RANDOMIZED) {
175
            mt_srand($arguments['randomOrderSeed']);
176
        }
177
 
178
        if ($arguments['cacheResult']) {
179
            if (!isset($arguments['cacheResultFile'])) {
180
                if (isset($arguments['configurationObject'])) {
181
                    assert($arguments['configurationObject'] instanceof Configuration);
182
 
183
                    $cacheLocation = $arguments['configurationObject']->filename();
184
                } else {
185
                    $cacheLocation = $_SERVER['PHP_SELF'];
186
                }
187
 
188
                $arguments['cacheResultFile'] = null;
189
 
190
                $cacheResultFile = realpath($cacheLocation);
191
 
192
                if ($cacheResultFile !== false) {
193
                    $arguments['cacheResultFile'] = dirname($cacheResultFile);
194
                }
195
            }
196
 
197
            $cache = new DefaultTestResultCache($arguments['cacheResultFile']);
198
 
199
            $this->addExtension(new ResultCacheExtension($cache));
200
        }
201
 
202
        if ($arguments['executionOrder'] !== TestSuiteSorter::ORDER_DEFAULT || $arguments['executionOrderDefects'] !== TestSuiteSorter::ORDER_DEFAULT || $arguments['resolveDependencies']) {
203
            $cache = $cache ?? new NullTestResultCache;
204
 
205
            $cache->load();
206
 
207
            $sorter = new TestSuiteSorter($cache);
208
 
209
            $sorter->reorderTestsInSuite($suite, $arguments['executionOrder'], $arguments['resolveDependencies'], $arguments['executionOrderDefects']);
210
            $originalExecutionOrder = $sorter->getOriginalExecutionOrder();
211
 
212
            unset($sorter);
213
        }
214
 
215
        if (is_int($arguments['repeat']) && $arguments['repeat'] > 0) {
216
            $_suite = new TestSuite;
217
 
218
            /* @noinspection PhpUnusedLocalVariableInspection */
219
            foreach (range(1, $arguments['repeat']) as $step) {
220
                $_suite->addTest($suite);
221
            }
222
 
223
            $suite = $_suite;
224
 
225
            unset($_suite);
226
        }
227
 
228
        $result = $this->createTestResult();
229
 
230
        $listener       = new TestListenerAdapter;
231
        $listenerNeeded = false;
232
 
233
        foreach ($this->extensions as $extension) {
234
            if ($extension instanceof TestHook) {
235
                $listener->add($extension);
236
 
237
                $listenerNeeded = true;
238
            }
239
        }
240
 
241
        if ($listenerNeeded) {
242
            $result->addListener($listener);
243
        }
244
 
245
        unset($listener, $listenerNeeded);
246
 
247
        if ($arguments['convertDeprecationsToExceptions']) {
248
            $result->convertDeprecationsToExceptions(true);
249
        }
250
 
251
        if (!$arguments['convertErrorsToExceptions']) {
252
            $result->convertErrorsToExceptions(false);
253
        }
254
 
255
        if (!$arguments['convertNoticesToExceptions']) {
256
            $result->convertNoticesToExceptions(false);
257
        }
258
 
259
        if (!$arguments['convertWarningsToExceptions']) {
260
            $result->convertWarningsToExceptions(false);
261
        }
262
 
263
        if ($arguments['stopOnError']) {
264
            $result->stopOnError(true);
265
        }
266
 
267
        if ($arguments['stopOnFailure']) {
268
            $result->stopOnFailure(true);
269
        }
270
 
271
        if ($arguments['stopOnWarning']) {
272
            $result->stopOnWarning(true);
273
        }
274
 
275
        if ($arguments['stopOnIncomplete']) {
276
            $result->stopOnIncomplete(true);
277
        }
278
 
279
        if ($arguments['stopOnRisky']) {
280
            $result->stopOnRisky(true);
281
        }
282
 
283
        if ($arguments['stopOnSkipped']) {
284
            $result->stopOnSkipped(true);
285
        }
286
 
287
        if ($arguments['stopOnDefect']) {
288
            $result->stopOnDefect(true);
289
        }
290
 
291
        if ($arguments['registerMockObjectsFromTestArgumentsRecursively']) {
292
            $result->setRegisterMockObjectsFromTestArgumentsRecursively(true);
293
        }
294
 
295
        if ($this->printer === null) {
296
            if (isset($arguments['printer'])) {
297
                if ($arguments['printer'] instanceof ResultPrinter) {
298
                    $this->printer = $arguments['printer'];
299
                } elseif (is_string($arguments['printer']) && class_exists($arguments['printer'], false)) {
300
                    try {
301
                        $reflector = new ReflectionClass($arguments['printer']);
302
 
303
                        if ($reflector->implementsInterface(ResultPrinter::class)) {
304
                            $this->printer = $this->createPrinter($arguments['printer'], $arguments);
305
                        }
306
 
307
                        // @codeCoverageIgnoreStart
308
                    } catch (ReflectionException $e) {
309
                        throw new Exception(
310
                            $e->getMessage(),
311
                            $e->getCode(),
312
                            $e
313
                        );
314
                    }
315
                    // @codeCoverageIgnoreEnd
316
                }
317
            } else {
318
                $this->printer = $this->createPrinter(DefaultResultPrinter::class, $arguments);
319
            }
320
        }
321
 
322
        if (isset($originalExecutionOrder) && $this->printer instanceof CliTestDoxPrinter) {
323
            assert($this->printer instanceof CliTestDoxPrinter);
324
 
325
            $this->printer->setOriginalExecutionOrder($originalExecutionOrder);
326
            $this->printer->setShowProgressAnimation(!$arguments['noInteraction']);
327
        }
328
 
329
        $this->write(Version::getVersionString() . "\n");
330
 
331
        foreach ($arguments['listeners'] as $listener) {
332
            $result->addListener($listener);
333
        }
334
 
335
        $result->addListener($this->printer);
336
 
337
        $coverageFilterFromConfigurationFile = false;
338
        $coverageFilterFromOption            = false;
339
        $codeCoverageReports                 = 0;
340
 
341
        if (isset($arguments['testdoxHTMLFile'])) {
342
            $result->addListener(
343
                new HtmlResultPrinter(
344
                    $arguments['testdoxHTMLFile'],
345
                    $arguments['testdoxGroups'],
346
                    $arguments['testdoxExcludeGroups']
347
                )
348
            );
349
        }
350
 
351
        if (isset($arguments['testdoxTextFile'])) {
352
            $result->addListener(
353
                new TextResultPrinter(
354
                    $arguments['testdoxTextFile'],
355
                    $arguments['testdoxGroups'],
356
                    $arguments['testdoxExcludeGroups']
357
                )
358
            );
359
        }
360
 
361
        if (isset($arguments['testdoxXMLFile'])) {
362
            $result->addListener(
363
                new XmlResultPrinter(
364
                    $arguments['testdoxXMLFile']
365
                )
366
            );
367
        }
368
 
369
        if (isset($arguments['teamcityLogfile'])) {
370
            $result->addListener(
371
                new TeamCity($arguments['teamcityLogfile'])
372
            );
373
        }
374
 
375
        if (isset($arguments['junitLogfile'])) {
376
            $result->addListener(
377
                new JUnit(
378
                    $arguments['junitLogfile'],
379
                    $arguments['reportUselessTests']
380
                )
381
            );
382
        }
383
 
384
        if (isset($arguments['coverageClover'])) {
385
            $codeCoverageReports++;
386
        }
387
 
388
        if (isset($arguments['coverageCobertura'])) {
389
            $codeCoverageReports++;
390
        }
391
 
392
        if (isset($arguments['coverageCrap4J'])) {
393
            $codeCoverageReports++;
394
        }
395
 
396
        if (isset($arguments['coverageHtml'])) {
397
            $codeCoverageReports++;
398
        }
399
 
400
        if (isset($arguments['coveragePHP'])) {
401
            $codeCoverageReports++;
402
        }
403
 
404
        if (isset($arguments['coverageText'])) {
405
            $codeCoverageReports++;
406
        }
407
 
408
        if (isset($arguments['coverageXml'])) {
409
            $codeCoverageReports++;
410
        }
411
 
412
        if ($codeCoverageReports > 0 || isset($arguments['xdebugFilterFile'])) {
413
            if (isset($arguments['coverageFilter'])) {
414
                if (!is_array($arguments['coverageFilter'])) {
415
                    $coverageFilterDirectories = [$arguments['coverageFilter']];
416
                } else {
417
                    $coverageFilterDirectories = $arguments['coverageFilter'];
418
                }
419
 
420
                foreach ($coverageFilterDirectories as $coverageFilterDirectory) {
421
                    $this->codeCoverageFilter->includeDirectory($coverageFilterDirectory);
422
                }
423
 
424
                $coverageFilterFromOption = true;
425
            }
426
 
427
            if (isset($arguments['configurationObject'])) {
428
                assert($arguments['configurationObject'] instanceof Configuration);
429
 
430
                $codeCoverageConfiguration = $arguments['configurationObject']->codeCoverage();
431
 
432
                if ($codeCoverageConfiguration->hasNonEmptyListOfFilesToBeIncludedInCodeCoverageReport()) {
433
                    $coverageFilterFromConfigurationFile = true;
434
 
435
                    (new FilterMapper)->map(
436
                        $this->codeCoverageFilter,
437
                        $codeCoverageConfiguration
438
                    );
439
                }
440
            }
441
        }
442
 
443
        if ($codeCoverageReports > 0) {
444
            try {
445
                if (isset($codeCoverageConfiguration) &&
446
                    ($codeCoverageConfiguration->pathCoverage() || (isset($arguments['pathCoverage']) && $arguments['pathCoverage'] === true))) {
447
                    $codeCoverageDriver = (new Selector)->forLineAndPathCoverage($this->codeCoverageFilter);
448
                } else {
449
                    $codeCoverageDriver = (new Selector)->forLineCoverage($this->codeCoverageFilter);
450
                }
451
 
452
                $codeCoverage = new CodeCoverage(
453
                    $codeCoverageDriver,
454
                    $this->codeCoverageFilter
455
                );
456
 
457
                if (isset($codeCoverageConfiguration) && $codeCoverageConfiguration->hasCacheDirectory()) {
458
                    $codeCoverage->cacheStaticAnalysis($codeCoverageConfiguration->cacheDirectory()->path());
459
                }
460
 
461
                if (isset($arguments['coverageCacheDirectory'])) {
462
                    $codeCoverage->cacheStaticAnalysis($arguments['coverageCacheDirectory']);
463
                }
464
 
465
                $codeCoverage->excludeSubclassesOfThisClassFromUnintentionallyCoveredCodeCheck(Comparator::class);
466
 
467
                if ($arguments['strictCoverage']) {
468
                    $codeCoverage->enableCheckForUnintentionallyCoveredCode();
469
                }
470
 
471
                if (isset($arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'])) {
472
                    if ($arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage']) {
473
                        $codeCoverage->ignoreDeprecatedCode();
474
                    } else {
475
                        $codeCoverage->doNotIgnoreDeprecatedCode();
476
                    }
477
                }
478
 
479
                if (isset($arguments['disableCodeCoverageIgnore'])) {
480
                    if ($arguments['disableCodeCoverageIgnore']) {
481
                        $codeCoverage->disableAnnotationsForIgnoringCode();
482
                    } else {
483
                        $codeCoverage->enableAnnotationsForIgnoringCode();
484
                    }
485
                }
486
 
487
                if (isset($arguments['configurationObject'])) {
488
                    $codeCoverageConfiguration = $arguments['configurationObject']->codeCoverage();
489
 
490
                    if ($codeCoverageConfiguration->hasNonEmptyListOfFilesToBeIncludedInCodeCoverageReport()) {
491
                        if ($codeCoverageConfiguration->includeUncoveredFiles()) {
492
                            $codeCoverage->includeUncoveredFiles();
493
                        } else {
494
                            $codeCoverage->excludeUncoveredFiles();
495
                        }
496
 
497
                        if ($codeCoverageConfiguration->processUncoveredFiles()) {
498
                            $codeCoverage->processUncoveredFiles();
499
                        } else {
500
                            $codeCoverage->doNotProcessUncoveredFiles();
501
                        }
502
                    }
503
                }
504
 
505
                if ($this->codeCoverageFilter->isEmpty()) {
506
                    if (!$coverageFilterFromConfigurationFile && !$coverageFilterFromOption) {
507
                        $warnings[] = 'No filter is configured, code coverage will not be processed';
508
                    } else {
509
                        $warnings[] = 'Incorrect filter configuration, code coverage will not be processed';
510
                    }
511
 
512
                    unset($codeCoverage);
513
                }
514
            } catch (CodeCoverageException $e) {
515
                $warnings[] = $e->getMessage();
516
            }
517
        }
518
 
519
        if ($arguments['verbose']) {
520
            if (PHP_SAPI === 'phpdbg') {
521
                $this->writeMessage('Runtime', 'PHPDBG ' . PHP_VERSION);
522
            } else {
523
                $runtime = 'PHP ' . PHP_VERSION;
524
 
525
                if (isset($codeCoverageDriver)) {
526
                    $runtime .= ' with ' . $codeCoverageDriver->nameAndVersion();
527
                }
528
 
529
                $this->writeMessage('Runtime', $runtime);
530
            }
531
 
532
            if (isset($arguments['configurationObject'])) {
533
                assert($arguments['configurationObject'] instanceof Configuration);
534
 
535
                $this->writeMessage(
536
                    'Configuration',
537
                    $arguments['configurationObject']->filename()
538
                );
539
            }
540
 
541
            foreach ($arguments['loadedExtensions'] as $extension) {
542
                $this->writeMessage(
543
                    'Extension',
544
                    $extension
545
                );
546
            }
547
 
548
            foreach ($arguments['notLoadedExtensions'] as $extension) {
549
                $this->writeMessage(
550
                    'Extension',
551
                    $extension
552
                );
553
            }
554
        }
555
 
556
        if ($arguments['executionOrder'] === TestSuiteSorter::ORDER_RANDOMIZED) {
557
            $this->writeMessage(
558
                'Random Seed',
559
                (string) $arguments['randomOrderSeed']
560
            );
561
        }
562
 
563
        if (isset($tooFewColumnsRequested)) {
564
            $warnings[] = 'Less than 16 columns requested, number of columns set to 16';
565
        }
566
 
567
        if ((new Runtime)->discardsComments()) {
568
            $warnings[] = 'opcache.save_comments=0 set; annotations will not work';
569
        }
570
 
571
        if (isset($arguments['conflictBetweenPrinterClassAndTestdox'])) {
572
            $warnings[] = 'Directives printerClass and testdox are mutually exclusive';
573
        }
574
 
150 lars 575
        $warnings = array_merge($warnings, $suite->warnings());
576
        sort($warnings);
577
 
148 lars 578
        foreach ($warnings as $warning) {
579
            $this->writeMessage('Warning', $warning);
580
        }
581
 
582
        if (isset($arguments['configurationObject'])) {
583
            assert($arguments['configurationObject'] instanceof Configuration);
584
 
585
            if ($arguments['configurationObject']->hasValidationErrors()) {
586
                if ((new SchemaDetector)->detect($arguments['configurationObject']->filename())->detected()) {
587
                    $this->writeMessage('Warning', 'Your XML configuration validates against a deprecated schema.');
588
                    $this->writeMessage('Suggestion', 'Migrate your XML configuration using "--migrate-configuration"!');
589
                } else {
590
                    $this->write(
591
                        "\n  Warning - The configuration file did not pass validation!\n  The following problems have been detected:\n"
592
                    );
593
 
594
                    $this->write($arguments['configurationObject']->validationErrors());
595
 
596
                    $this->write("\n  Test results may not be as expected.\n\n");
597
                }
598
            }
599
        }
600
 
601
        if (isset($arguments['xdebugFilterFile'], $codeCoverageConfiguration)) {
602
            $this->write(PHP_EOL . 'Please note that --dump-xdebug-filter and --prepend are deprecated and will be removed in PHPUnit 10.' . PHP_EOL);
603
 
604
            $script = (new XdebugFilterScriptGenerator)->generate($codeCoverageConfiguration);
605
 
606
            if ($arguments['xdebugFilterFile'] !== 'php://stdout' && $arguments['xdebugFilterFile'] !== 'php://stderr' && !Filesystem::createDirectory(dirname($arguments['xdebugFilterFile']))) {
607
                $this->write(sprintf('Cannot write Xdebug filter script to %s ' . PHP_EOL, $arguments['xdebugFilterFile']));
608
 
609
                exit(self::EXCEPTION_EXIT);
610
            }
611
 
612
            file_put_contents($arguments['xdebugFilterFile'], $script);
613
 
614
            $this->write(sprintf('Wrote Xdebug filter script to %s ' . PHP_EOL . PHP_EOL, $arguments['xdebugFilterFile']));
615
 
616
            exit(self::SUCCESS_EXIT);
617
        }
618
 
619
        $this->write("\n");
620
 
621
        if (isset($codeCoverage)) {
622
            $result->setCodeCoverage($codeCoverage);
623
        }
624
 
625
        $result->beStrictAboutTestsThatDoNotTestAnything($arguments['reportUselessTests']);
626
        $result->beStrictAboutOutputDuringTests($arguments['disallowTestOutput']);
627
        $result->beStrictAboutTodoAnnotatedTests($arguments['disallowTodoAnnotatedTests']);
628
        $result->beStrictAboutResourceUsageDuringSmallTests($arguments['beStrictAboutResourceUsageDuringSmallTests']);
629
 
630
        if ($arguments['enforceTimeLimit'] === true && !(new Invoker)->canInvokeWithTimeout()) {
631
            $this->writeMessage('Error', 'PHP extension pcntl is required for enforcing time limits');
632
        }
633
 
634
        $result->enforceTimeLimit($arguments['enforceTimeLimit']);
635
        $result->setDefaultTimeLimit($arguments['defaultTimeLimit']);
636
        $result->setTimeoutForSmallTests($arguments['timeoutForSmallTests']);
637
        $result->setTimeoutForMediumTests($arguments['timeoutForMediumTests']);
638
        $result->setTimeoutForLargeTests($arguments['timeoutForLargeTests']);
639
 
640
        if (isset($arguments['forceCoversAnnotation']) && $arguments['forceCoversAnnotation'] === true) {
641
            $result->forceCoversAnnotation();
642
        }
643
 
644
        $this->processSuiteFilters($suite, $arguments);
645
        $suite->setRunTestInSeparateProcess($arguments['processIsolation']);
646
 
647
        foreach ($this->extensions as $extension) {
648
            if ($extension instanceof BeforeFirstTestHook) {
649
                $extension->executeBeforeFirstTest();
650
            }
651
        }
652
 
653
        $suite->run($result);
654
 
655
        foreach ($this->extensions as $extension) {
656
            if ($extension instanceof AfterLastTestHook) {
657
                $extension->executeAfterLastTest();
658
            }
659
        }
660
 
661
        $result->flushListeners();
662
        $this->printer->printResult($result);
663
 
664
        if (isset($codeCoverage)) {
665
            if (isset($arguments['coverageClover'])) {
666
                $this->codeCoverageGenerationStart('Clover XML');
667
 
668
                try {
669
                    $writer = new CloverReport;
670
                    $writer->process($codeCoverage, $arguments['coverageClover']);
671
 
672
                    $this->codeCoverageGenerationSucceeded();
673
 
674
                    unset($writer);
675
                } catch (CodeCoverageException $e) {
676
                    $this->codeCoverageGenerationFailed($e);
677
                }
678
            }
679
 
680
            if (isset($arguments['coverageCobertura'])) {
681
                $this->codeCoverageGenerationStart('Cobertura XML');
682
 
683
                try {
684
                    $writer = new CoberturaReport;
685
                    $writer->process($codeCoverage, $arguments['coverageCobertura']);
686
 
687
                    $this->codeCoverageGenerationSucceeded();
688
 
689
                    unset($writer);
690
                } catch (CodeCoverageException $e) {
691
                    $this->codeCoverageGenerationFailed($e);
692
                }
693
            }
694
 
695
            if (isset($arguments['coverageCrap4J'])) {
696
                $this->codeCoverageGenerationStart('Crap4J XML');
697
 
698
                try {
699
                    $writer = new Crap4jReport($arguments['crap4jThreshold']);
700
                    $writer->process($codeCoverage, $arguments['coverageCrap4J']);
701
 
702
                    $this->codeCoverageGenerationSucceeded();
703
 
704
                    unset($writer);
705
                } catch (CodeCoverageException $e) {
706
                    $this->codeCoverageGenerationFailed($e);
707
                }
708
            }
709
 
710
            if (isset($arguments['coverageHtml'])) {
711
                $this->codeCoverageGenerationStart('HTML');
712
 
713
                try {
714
                    $writer = new HtmlReport(
715
                        $arguments['reportLowUpperBound'],
716
                        $arguments['reportHighLowerBound'],
717
                        sprintf(
718
                            ' and <a href="https://phpunit.de/">PHPUnit %s</a>',
719
                            Version::id()
720
                        )
721
                    );
722
 
723
                    $writer->process($codeCoverage, $arguments['coverageHtml']);
724
 
725
                    $this->codeCoverageGenerationSucceeded();
726
 
727
                    unset($writer);
728
                } catch (CodeCoverageException $e) {
729
                    $this->codeCoverageGenerationFailed($e);
730
                }
731
            }
732
 
733
            if (isset($arguments['coveragePHP'])) {
734
                $this->codeCoverageGenerationStart('PHP');
735
 
736
                try {
737
                    $writer = new PhpReport;
738
                    $writer->process($codeCoverage, $arguments['coveragePHP']);
739
 
740
                    $this->codeCoverageGenerationSucceeded();
741
 
742
                    unset($writer);
743
                } catch (CodeCoverageException $e) {
744
                    $this->codeCoverageGenerationFailed($e);
745
                }
746
            }
747
 
748
            if (isset($arguments['coverageText'])) {
749
                if ($arguments['coverageText'] === 'php://stdout') {
750
                    $outputStream = $this->printer;
751
                    $colors       = $arguments['colors'] && $arguments['colors'] !== DefaultResultPrinter::COLOR_NEVER;
752
                } else {
753
                    $outputStream = new Printer($arguments['coverageText']);
754
                    $colors       = false;
755
                }
756
 
757
                $processor = new TextReport(
758
                    $arguments['reportLowUpperBound'],
759
                    $arguments['reportHighLowerBound'],
760
                    $arguments['coverageTextShowUncoveredFiles'],
761
                    $arguments['coverageTextShowOnlySummary']
762
                );
763
 
764
                $outputStream->write(
765
                    $processor->process($codeCoverage, $colors)
766
                );
767
            }
768
 
769
            if (isset($arguments['coverageXml'])) {
770
                $this->codeCoverageGenerationStart('PHPUnit XML');
771
 
772
                try {
773
                    $writer = new XmlReport(Version::id());
774
                    $writer->process($codeCoverage, $arguments['coverageXml']);
775
 
776
                    $this->codeCoverageGenerationSucceeded();
777
 
778
                    unset($writer);
779
                } catch (CodeCoverageException $e) {
780
                    $this->codeCoverageGenerationFailed($e);
781
                }
782
            }
783
        }
784
 
785
        if ($exit) {
786
            if (isset($arguments['failOnEmptyTestSuite']) && $arguments['failOnEmptyTestSuite'] === true && count($result) === 0) {
787
                exit(self::FAILURE_EXIT);
788
            }
789
 
790
            if ($result->wasSuccessfulIgnoringWarnings()) {
791
                if ($arguments['failOnRisky'] && !$result->allHarmless()) {
792
                    exit(self::FAILURE_EXIT);
793
                }
794
 
795
                if ($arguments['failOnWarning'] && $result->warningCount() > 0) {
796
                    exit(self::FAILURE_EXIT);
797
                }
798
 
799
                if ($arguments['failOnIncomplete'] && $result->notImplementedCount() > 0) {
800
                    exit(self::FAILURE_EXIT);
801
                }
802
 
803
                if ($arguments['failOnSkipped'] && $result->skippedCount() > 0) {
804
                    exit(self::FAILURE_EXIT);
805
                }
806
 
807
                exit(self::SUCCESS_EXIT);
808
            }
809
 
810
            if ($result->errorCount() > 0) {
811
                exit(self::EXCEPTION_EXIT);
812
            }
813
 
814
            if ($result->failureCount() > 0) {
815
                exit(self::FAILURE_EXIT);
816
            }
817
        }
818
 
819
        return $result;
820
    }
821
 
822
    /**
823
     * Returns the loader to be used.
824
     */
825
    public function getLoader(): TestSuiteLoader
826
    {
827
        if ($this->loader === null) {
828
            $this->loader = new StandardTestSuiteLoader;
829
        }
830
 
831
        return $this->loader;
832
    }
833
 
834
    public function addExtension(Hook $extension): void
835
    {
836
        $this->extensions[] = $extension;
837
    }
838
 
839
    /**
840
     * Override to define how to handle a failed loading of
841
     * a test suite.
842
     */
843
    protected function runFailed(string $message): void
844
    {
845
        $this->write($message . PHP_EOL);
846
 
847
        exit(self::FAILURE_EXIT);
848
    }
849
 
850
    private function createTestResult(): TestResult
851
    {
852
        return new TestResult;
853
    }
854
 
855
    private function write(string $buffer): void
856
    {
857
        if (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg') {
858
            $buffer = htmlspecialchars($buffer);
859
        }
860
 
861
        if ($this->printer !== null) {
862
            $this->printer->write($buffer);
863
        } else {
864
            print $buffer;
865
        }
866
    }
867
 
868
    /**
869
     * @throws \PHPUnit\TextUI\XmlConfiguration\Exception
870
     * @throws Exception
871
     */
872
    private function handleConfiguration(array &$arguments): void
873
    {
874
        if (!isset($arguments['configurationObject']) && isset($arguments['configuration'])) {
875
            $arguments['configurationObject'] = (new Loader)->load($arguments['configuration']);
876
        }
877
 
878
        if (!isset($arguments['warnings'])) {
879
            $arguments['warnings'] = [];
880
        }
881
 
882
        $arguments['debug']     = $arguments['debug'] ?? false;
883
        $arguments['filter']    = $arguments['filter'] ?? false;
884
        $arguments['listeners'] = $arguments['listeners'] ?? [];
885
 
886
        if (isset($arguments['configurationObject'])) {
887
            (new PhpHandler)->handle($arguments['configurationObject']->php());
888
 
889
            $codeCoverageConfiguration = $arguments['configurationObject']->codeCoverage();
890
 
891
            if (!isset($arguments['noCoverage'])) {
892
                if (!isset($arguments['coverageClover']) && $codeCoverageConfiguration->hasClover()) {
893
                    $arguments['coverageClover'] = $codeCoverageConfiguration->clover()->target()->path();
894
                }
895
 
896
                if (!isset($arguments['coverageCobertura']) && $codeCoverageConfiguration->hasCobertura()) {
897
                    $arguments['coverageCobertura'] = $codeCoverageConfiguration->cobertura()->target()->path();
898
                }
899
 
900
                if (!isset($arguments['coverageCrap4J']) && $codeCoverageConfiguration->hasCrap4j()) {
901
                    $arguments['coverageCrap4J'] = $codeCoverageConfiguration->crap4j()->target()->path();
902
 
903
                    if (!isset($arguments['crap4jThreshold'])) {
904
                        $arguments['crap4jThreshold'] = $codeCoverageConfiguration->crap4j()->threshold();
905
                    }
906
                }
907
 
908
                if (!isset($arguments['coverageHtml']) && $codeCoverageConfiguration->hasHtml()) {
909
                    $arguments['coverageHtml'] = $codeCoverageConfiguration->html()->target()->path();
910
 
911
                    if (!isset($arguments['reportLowUpperBound'])) {
912
                        $arguments['reportLowUpperBound'] = $codeCoverageConfiguration->html()->lowUpperBound();
913
                    }
914
 
915
                    if (!isset($arguments['reportHighLowerBound'])) {
916
                        $arguments['reportHighLowerBound'] = $codeCoverageConfiguration->html()->highLowerBound();
917
                    }
918
                }
919
 
920
                if (!isset($arguments['coveragePHP']) && $codeCoverageConfiguration->hasPhp()) {
921
                    $arguments['coveragePHP'] = $codeCoverageConfiguration->php()->target()->path();
922
                }
923
 
924
                if (!isset($arguments['coverageText']) && $codeCoverageConfiguration->hasText()) {
925
                    $arguments['coverageText']                   = $codeCoverageConfiguration->text()->target()->path();
926
                    $arguments['coverageTextShowUncoveredFiles'] = $codeCoverageConfiguration->text()->showUncoveredFiles();
927
                    $arguments['coverageTextShowOnlySummary']    = $codeCoverageConfiguration->text()->showOnlySummary();
928
                }
929
 
930
                if (!isset($arguments['coverageXml']) && $codeCoverageConfiguration->hasXml()) {
931
                    $arguments['coverageXml'] = $codeCoverageConfiguration->xml()->target()->path();
932
                }
933
            }
934
 
935
            $phpunitConfiguration = $arguments['configurationObject']->phpunit();
936
 
937
            $arguments['backupGlobals']                                   = $arguments['backupGlobals'] ?? $phpunitConfiguration->backupGlobals();
938
            $arguments['backupStaticAttributes']                          = $arguments['backupStaticAttributes'] ?? $phpunitConfiguration->backupStaticAttributes();
939
            $arguments['beStrictAboutChangesToGlobalState']               = $arguments['beStrictAboutChangesToGlobalState'] ?? $phpunitConfiguration->beStrictAboutChangesToGlobalState();
940
            $arguments['cacheResult']                                     = $arguments['cacheResult'] ?? $phpunitConfiguration->cacheResult();
941
            $arguments['colors']                                          = $arguments['colors'] ?? $phpunitConfiguration->colors();
942
            $arguments['convertDeprecationsToExceptions']                 = $arguments['convertDeprecationsToExceptions'] ?? $phpunitConfiguration->convertDeprecationsToExceptions();
943
            $arguments['convertErrorsToExceptions']                       = $arguments['convertErrorsToExceptions'] ?? $phpunitConfiguration->convertErrorsToExceptions();
944
            $arguments['convertNoticesToExceptions']                      = $arguments['convertNoticesToExceptions'] ?? $phpunitConfiguration->convertNoticesToExceptions();
945
            $arguments['convertWarningsToExceptions']                     = $arguments['convertWarningsToExceptions'] ?? $phpunitConfiguration->convertWarningsToExceptions();
946
            $arguments['processIsolation']                                = $arguments['processIsolation'] ?? $phpunitConfiguration->processIsolation();
947
            $arguments['stopOnDefect']                                    = $arguments['stopOnDefect'] ?? $phpunitConfiguration->stopOnDefect();
948
            $arguments['stopOnError']                                     = $arguments['stopOnError'] ?? $phpunitConfiguration->stopOnError();
949
            $arguments['stopOnFailure']                                   = $arguments['stopOnFailure'] ?? $phpunitConfiguration->stopOnFailure();
950
            $arguments['stopOnWarning']                                   = $arguments['stopOnWarning'] ?? $phpunitConfiguration->stopOnWarning();
951
            $arguments['stopOnIncomplete']                                = $arguments['stopOnIncomplete'] ?? $phpunitConfiguration->stopOnIncomplete();
952
            $arguments['stopOnRisky']                                     = $arguments['stopOnRisky'] ?? $phpunitConfiguration->stopOnRisky();
953
            $arguments['stopOnSkipped']                                   = $arguments['stopOnSkipped'] ?? $phpunitConfiguration->stopOnSkipped();
954
            $arguments['failOnEmptyTestSuite']                            = $arguments['failOnEmptyTestSuite'] ?? $phpunitConfiguration->failOnEmptyTestSuite();
955
            $arguments['failOnIncomplete']                                = $arguments['failOnIncomplete'] ?? $phpunitConfiguration->failOnIncomplete();
956
            $arguments['failOnRisky']                                     = $arguments['failOnRisky'] ?? $phpunitConfiguration->failOnRisky();
957
            $arguments['failOnSkipped']                                   = $arguments['failOnSkipped'] ?? $phpunitConfiguration->failOnSkipped();
958
            $arguments['failOnWarning']                                   = $arguments['failOnWarning'] ?? $phpunitConfiguration->failOnWarning();
959
            $arguments['enforceTimeLimit']                                = $arguments['enforceTimeLimit'] ?? $phpunitConfiguration->enforceTimeLimit();
960
            $arguments['defaultTimeLimit']                                = $arguments['defaultTimeLimit'] ?? $phpunitConfiguration->defaultTimeLimit();
961
            $arguments['timeoutForSmallTests']                            = $arguments['timeoutForSmallTests'] ?? $phpunitConfiguration->timeoutForSmallTests();
962
            $arguments['timeoutForMediumTests']                           = $arguments['timeoutForMediumTests'] ?? $phpunitConfiguration->timeoutForMediumTests();
963
            $arguments['timeoutForLargeTests']                            = $arguments['timeoutForLargeTests'] ?? $phpunitConfiguration->timeoutForLargeTests();
964
            $arguments['reportUselessTests']                              = $arguments['reportUselessTests'] ?? $phpunitConfiguration->beStrictAboutTestsThatDoNotTestAnything();
965
            $arguments['strictCoverage']                                  = $arguments['strictCoverage'] ?? $phpunitConfiguration->beStrictAboutCoversAnnotation();
966
            $arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage']       = $arguments['ignoreDeprecatedCodeUnitsFromCodeCoverage'] ?? $codeCoverageConfiguration->ignoreDeprecatedCodeUnits();
967
            $arguments['disallowTestOutput']                              = $arguments['disallowTestOutput'] ?? $phpunitConfiguration->beStrictAboutOutputDuringTests();
968
            $arguments['disallowTodoAnnotatedTests']                      = $arguments['disallowTodoAnnotatedTests'] ?? $phpunitConfiguration->beStrictAboutTodoAnnotatedTests();
969
            $arguments['beStrictAboutResourceUsageDuringSmallTests']      = $arguments['beStrictAboutResourceUsageDuringSmallTests'] ?? $phpunitConfiguration->beStrictAboutResourceUsageDuringSmallTests();
970
            $arguments['verbose']                                         = $arguments['verbose'] ?? $phpunitConfiguration->verbose();
971
            $arguments['reverseDefectList']                               = $arguments['reverseDefectList'] ?? $phpunitConfiguration->reverseDefectList();
972
            $arguments['forceCoversAnnotation']                           = $arguments['forceCoversAnnotation'] ?? $phpunitConfiguration->forceCoversAnnotation();
973
            $arguments['disableCodeCoverageIgnore']                       = $arguments['disableCodeCoverageIgnore'] ?? $codeCoverageConfiguration->disableCodeCoverageIgnore();
974
            $arguments['registerMockObjectsFromTestArgumentsRecursively'] = $arguments['registerMockObjectsFromTestArgumentsRecursively'] ?? $phpunitConfiguration->registerMockObjectsFromTestArgumentsRecursively();
975
            $arguments['noInteraction']                                   = $arguments['noInteraction'] ?? $phpunitConfiguration->noInteraction();
976
            $arguments['executionOrder']                                  = $arguments['executionOrder'] ?? $phpunitConfiguration->executionOrder();
977
            $arguments['resolveDependencies']                             = $arguments['resolveDependencies'] ?? $phpunitConfiguration->resolveDependencies();
978
 
979
            if (!isset($arguments['bootstrap']) && $phpunitConfiguration->hasBootstrap()) {
980
                $arguments['bootstrap'] = $phpunitConfiguration->bootstrap();
981
            }
982
 
983
            if (!isset($arguments['cacheResultFile']) && $phpunitConfiguration->hasCacheResultFile()) {
984
                $arguments['cacheResultFile'] = $phpunitConfiguration->cacheResultFile();
985
            }
986
 
987
            if (!isset($arguments['executionOrderDefects'])) {
988
                $arguments['executionOrderDefects'] = $phpunitConfiguration->defectsFirst() ? TestSuiteSorter::ORDER_DEFECTS_FIRST : TestSuiteSorter::ORDER_DEFAULT;
989
            }
990
 
991
            if ($phpunitConfiguration->conflictBetweenPrinterClassAndTestdox()) {
992
                $arguments['conflictBetweenPrinterClassAndTestdox'] = true;
993
            }
994
 
995
            $groupCliArgs = [];
996
 
997
            if (!empty($arguments['groups'])) {
998
                $groupCliArgs = $arguments['groups'];
999
            }
1000
 
1001
            $groupConfiguration = $arguments['configurationObject']->groups();
1002
 
1003
            if (!isset($arguments['groups']) && $groupConfiguration->hasInclude()) {
1004
                $arguments['groups'] = $groupConfiguration->include()->asArrayOfStrings();
1005
            }
1006
 
1007
            if (!isset($arguments['excludeGroups']) && $groupConfiguration->hasExclude()) {
1008
                $arguments['excludeGroups'] = array_diff($groupConfiguration->exclude()->asArrayOfStrings(), $groupCliArgs);
1009
            }
1010
 
621 lars 1011
            if (!isset($this->arguments['noExtensions'])) {
1012
                $extensionHandler = new ExtensionHandler;
148 lars 1013
 
621 lars 1014
                foreach ($arguments['configurationObject']->extensions() as $extension) {
1015
                    $extensionHandler->registerExtension($extension, $this);
1016
                }
148 lars 1017
 
621 lars 1018
                foreach ($arguments['configurationObject']->listeners() as $listener) {
1019
                    $arguments['listeners'][] = $extensionHandler->createTestListenerInstance($listener);
1020
                }
1021
 
1022
                unset($extensionHandler);
148 lars 1023
            }
1024
 
1025
            foreach ($arguments['unavailableExtensions'] as $extension) {
1026
                $arguments['warnings'][] = sprintf(
1027
                    'Extension "%s" is not available',
1028
                    $extension
1029
                );
1030
            }
1031
 
1032
            $loggingConfiguration = $arguments['configurationObject']->logging();
1033
 
1034
            if (!isset($arguments['noLogging'])) {
1035
                if ($loggingConfiguration->hasText()) {
1036
                    $arguments['listeners'][] = new DefaultResultPrinter(
1037
                        $loggingConfiguration->text()->target()->path(),
1038
                        true
1039
                    );
1040
                }
1041
 
1042
                if (!isset($arguments['teamcityLogfile']) && $loggingConfiguration->hasTeamCity()) {
1043
                    $arguments['teamcityLogfile'] = $loggingConfiguration->teamCity()->target()->path();
1044
                }
1045
 
1046
                if (!isset($arguments['junitLogfile']) && $loggingConfiguration->hasJunit()) {
1047
                    $arguments['junitLogfile'] = $loggingConfiguration->junit()->target()->path();
1048
                }
1049
 
1050
                if (!isset($arguments['testdoxHTMLFile']) && $loggingConfiguration->hasTestDoxHtml()) {
1051
                    $arguments['testdoxHTMLFile'] = $loggingConfiguration->testDoxHtml()->target()->path();
1052
                }
1053
 
1054
                if (!isset($arguments['testdoxTextFile']) && $loggingConfiguration->hasTestDoxText()) {
1055
                    $arguments['testdoxTextFile'] = $loggingConfiguration->testDoxText()->target()->path();
1056
                }
1057
 
1058
                if (!isset($arguments['testdoxXMLFile']) && $loggingConfiguration->hasTestDoxXml()) {
1059
                    $arguments['testdoxXMLFile'] = $loggingConfiguration->testDoxXml()->target()->path();
1060
                }
1061
            }
1062
 
1063
            $testdoxGroupConfiguration = $arguments['configurationObject']->testdoxGroups();
1064
 
1065
            if (!isset($arguments['testdoxGroups']) && $testdoxGroupConfiguration->hasInclude()) {
1066
                $arguments['testdoxGroups'] = $testdoxGroupConfiguration->include()->asArrayOfStrings();
1067
            }
1068
 
1069
            if (!isset($arguments['testdoxExcludeGroups']) && $testdoxGroupConfiguration->hasExclude()) {
1070
                $arguments['testdoxExcludeGroups'] = $testdoxGroupConfiguration->exclude()->asArrayOfStrings();
1071
            }
1072
        }
1073
 
1074
        $extensionHandler = new ExtensionHandler;
1075
 
1076
        foreach ($arguments['extensions'] as $extension) {
1077
            $extensionHandler->registerExtension($extension, $this);
1078
        }
1079
 
1080
        unset($extensionHandler);
1081
 
1082
        $arguments['backupGlobals']                                   = $arguments['backupGlobals'] ?? null;
1083
        $arguments['backupStaticAttributes']                          = $arguments['backupStaticAttributes'] ?? null;
1084
        $arguments['beStrictAboutChangesToGlobalState']               = $arguments['beStrictAboutChangesToGlobalState'] ?? null;
1085
        $arguments['beStrictAboutResourceUsageDuringSmallTests']      = $arguments['beStrictAboutResourceUsageDuringSmallTests'] ?? false;
1086
        $arguments['cacheResult']                                     = $arguments['cacheResult'] ?? true;
1087
        $arguments['colors']                                          = $arguments['colors'] ?? DefaultResultPrinter::COLOR_DEFAULT;
1088
        $arguments['columns']                                         = $arguments['columns'] ?? 80;
1089
        $arguments['convertDeprecationsToExceptions']                 = $arguments['convertDeprecationsToExceptions'] ?? false;
1090
        $arguments['convertErrorsToExceptions']                       = $arguments['convertErrorsToExceptions'] ?? true;
1091
        $arguments['convertNoticesToExceptions']                      = $arguments['convertNoticesToExceptions'] ?? true;
1092
        $arguments['convertWarningsToExceptions']                     = $arguments['convertWarningsToExceptions'] ?? true;
1093
        $arguments['crap4jThreshold']                                 = $arguments['crap4jThreshold'] ?? 30;
1094
        $arguments['disallowTestOutput']                              = $arguments['disallowTestOutput'] ?? false;
1095
        $arguments['disallowTodoAnnotatedTests']                      = $arguments['disallowTodoAnnotatedTests'] ?? false;
1096
        $arguments['defaultTimeLimit']                                = $arguments['defaultTimeLimit'] ?? 0;
1097
        $arguments['enforceTimeLimit']                                = $arguments['enforceTimeLimit'] ?? false;
1098
        $arguments['excludeGroups']                                   = $arguments['excludeGroups'] ?? [];
1099
        $arguments['executionOrder']                                  = $arguments['executionOrder'] ?? TestSuiteSorter::ORDER_DEFAULT;
1100
        $arguments['executionOrderDefects']                           = $arguments['executionOrderDefects'] ?? TestSuiteSorter::ORDER_DEFAULT;
1101
        $arguments['failOnIncomplete']                                = $arguments['failOnIncomplete'] ?? false;
1102
        $arguments['failOnRisky']                                     = $arguments['failOnRisky'] ?? false;
1103
        $arguments['failOnSkipped']                                   = $arguments['failOnSkipped'] ?? false;
1104
        $arguments['failOnWarning']                                   = $arguments['failOnWarning'] ?? false;
1105
        $arguments['groups']                                          = $arguments['groups'] ?? [];
1106
        $arguments['noInteraction']                                   = $arguments['noInteraction'] ?? false;
1107
        $arguments['processIsolation']                                = $arguments['processIsolation'] ?? false;
1108
        $arguments['randomOrderSeed']                                 = $arguments['randomOrderSeed'] ?? time();
1109
        $arguments['registerMockObjectsFromTestArgumentsRecursively'] = $arguments['registerMockObjectsFromTestArgumentsRecursively'] ?? false;
1110
        $arguments['repeat']                                          = $arguments['repeat'] ?? false;
1111
        $arguments['reportHighLowerBound']                            = $arguments['reportHighLowerBound'] ?? 90;
1112
        $arguments['reportLowUpperBound']                             = $arguments['reportLowUpperBound'] ?? 50;
1113
        $arguments['reportUselessTests']                              = $arguments['reportUselessTests'] ?? true;
1114
        $arguments['reverseList']                                     = $arguments['reverseList'] ?? false;
1115
        $arguments['resolveDependencies']                             = $arguments['resolveDependencies'] ?? true;
1116
        $arguments['stopOnError']                                     = $arguments['stopOnError'] ?? false;
1117
        $arguments['stopOnFailure']                                   = $arguments['stopOnFailure'] ?? false;
1118
        $arguments['stopOnIncomplete']                                = $arguments['stopOnIncomplete'] ?? false;
1119
        $arguments['stopOnRisky']                                     = $arguments['stopOnRisky'] ?? false;
1120
        $arguments['stopOnSkipped']                                   = $arguments['stopOnSkipped'] ?? false;
1121
        $arguments['stopOnWarning']                                   = $arguments['stopOnWarning'] ?? false;
1122
        $arguments['stopOnDefect']                                    = $arguments['stopOnDefect'] ?? false;
1123
        $arguments['strictCoverage']                                  = $arguments['strictCoverage'] ?? false;
1124
        $arguments['testdoxExcludeGroups']                            = $arguments['testdoxExcludeGroups'] ?? [];
1125
        $arguments['testdoxGroups']                                   = $arguments['testdoxGroups'] ?? [];
1126
        $arguments['timeoutForLargeTests']                            = $arguments['timeoutForLargeTests'] ?? 60;
1127
        $arguments['timeoutForMediumTests']                           = $arguments['timeoutForMediumTests'] ?? 10;
1128
        $arguments['timeoutForSmallTests']                            = $arguments['timeoutForSmallTests'] ?? 1;
1129
        $arguments['verbose']                                         = $arguments['verbose'] ?? false;
1130
 
1131
        if ($arguments['reportLowUpperBound'] > $arguments['reportHighLowerBound']) {
1132
            $arguments['reportLowUpperBound']  = 50;
1133
            $arguments['reportHighLowerBound'] = 90;
1134
        }
1135
    }
1136
 
1137
    private function processSuiteFilters(TestSuite $suite, array $arguments): void
1138
    {
1139
        if (!$arguments['filter'] &&
1140
            empty($arguments['groups']) &&
1141
            empty($arguments['excludeGroups']) &&
1142
            empty($arguments['testsCovering']) &&
1143
            empty($arguments['testsUsing'])) {
1144
            return;
1145
        }
1146
 
1147
        $filterFactory = new Factory;
1148
 
1149
        if (!empty($arguments['excludeGroups'])) {
1150
            $filterFactory->addFilter(
1151
                new ReflectionClass(ExcludeGroupFilterIterator::class),
1152
                $arguments['excludeGroups']
1153
            );
1154
        }
1155
 
1156
        if (!empty($arguments['groups'])) {
1157
            $filterFactory->addFilter(
1158
                new ReflectionClass(IncludeGroupFilterIterator::class),
1159
                $arguments['groups']
1160
            );
1161
        }
1162
 
1163
        if (!empty($arguments['testsCovering'])) {
1164
            $filterFactory->addFilter(
1165
                new ReflectionClass(IncludeGroupFilterIterator::class),
1166
                array_map(
1167
                    static function (string $name): string
1168
                    {
1169
                        return '__phpunit_covers_' . $name;
1170
                    },
1171
                    $arguments['testsCovering']
1172
                )
1173
            );
1174
        }
1175
 
1176
        if (!empty($arguments['testsUsing'])) {
1177
            $filterFactory->addFilter(
1178
                new ReflectionClass(IncludeGroupFilterIterator::class),
1179
                array_map(
1180
                    static function (string $name): string
1181
                    {
1182
                        return '__phpunit_uses_' . $name;
1183
                    },
1184
                    $arguments['testsUsing']
1185
                )
1186
            );
1187
        }
1188
 
1189
        if ($arguments['filter']) {
1190
            $filterFactory->addFilter(
1191
                new ReflectionClass(NameFilterIterator::class),
1192
                $arguments['filter']
1193
            );
1194
        }
1195
 
1196
        $suite->injectFilter($filterFactory);
1197
    }
1198
 
1199
    private function writeMessage(string $type, string $message): void
1200
    {
1201
        if (!$this->messagePrinted) {
1202
            $this->write("\n");
1203
        }
1204
 
1205
        $this->write(
1206
            sprintf(
1207
                "%-15s%s\n",
1208
                $type . ':',
1209
                $message
1210
            )
1211
        );
1212
 
1213
        $this->messagePrinted = true;
1214
    }
1215
 
1216
    private function createPrinter(string $class, array $arguments): ResultPrinter
1217
    {
1218
        $object = new $class(
1219
            (isset($arguments['stderr']) && $arguments['stderr'] === true) ? 'php://stderr' : null,
1220
            $arguments['verbose'],
1221
            $arguments['colors'],
1222
            $arguments['debug'],
1223
            $arguments['columns'],
1224
            $arguments['reverseList']
1225
        );
1226
 
1227
        assert($object instanceof ResultPrinter);
1228
 
1229
        return $object;
1230
    }
1231
 
1232
    private function codeCoverageGenerationStart(string $format): void
1233
    {
1234
        $this->write(
1235
            sprintf(
1236
                "\nGenerating code coverage report in %s format ... ",
1237
                $format
1238
            )
1239
        );
1240
 
1241
        $this->timer->start();
1242
    }
1243
 
1244
    private function codeCoverageGenerationSucceeded(): void
1245
    {
1246
        $this->write(
1247
            sprintf(
1248
                "done [%s]\n",
1249
                $this->timer->stop()->asString()
1250
            )
1251
        );
1252
    }
1253
 
1254
    private function codeCoverageGenerationFailed(\Exception $e): void
1255
    {
1256
        $this->write(
1257
            sprintf(
1258
                "failed [%s]\n%s\n",
1259
                $this->timer->stop()->asString(),
1260
                $e->getMessage()
1261
            )
1262
        );
1263
    }
1264
}