"Fossies" - the Fresh Open Source Software Archive

Member "phpunit-9.0.1/src/TextUI/TestRunner.php" (13 Feb 2020, 49355 Bytes) of package /linux/www/phpunit-9.0.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) PHP source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "TestRunner.php" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 9.0.0_vs_9.0.1.

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