Subversion-Projekte lars-tiefland.laravel_shop

Revision

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