Completed
Push — master ( 01e071...007090 )
by Ehsan
01:46 queued 13s
created

Command::handleArguments()   F

Complexity

Conditions 127
Paths > 20000

Size

Total Lines 522
Code Lines 352

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 127
eloc 352
nc 774637946
nop 1
dl 0
loc 522
rs 2
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/*
3
 * This file is part of PHPUnit.
4
 *
5
 * (c) Sebastian Bergmann <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace PHPUnit\TextUI;
12
13
use File_Iterator_Facade;
14
use PharIo\Manifest\ApplicationName;
15
use PharIo\Manifest\ManifestLoader;
16
use PharIo\Version\Version as PharIoVersion;
17
use PharIo\Manifest\Exception as ManifestException;
18
use PHPUnit\Framework\Exception;
19
use PHPUnit\Framework\TestSuite;
20
use PHPUnit\Framework\Test;
21
use PHPUnit\Framework\TestListener;
22
use PHPUnit\Runner\PhptTestCase;
23
use PHPUnit\Runner\StandardTestSuiteLoader;
24
use PHPUnit\Runner\Version;
25
use PHPUnit\Runner\TestSuiteLoader;
26
use PHPUnit\Util\Configuration;
27
use PHPUnit\Util\ConfigurationGenerator;
28
use PHPUnit\Util\Fileloader;
29
use PHPUnit\Util\Filesystem;
30
use PHPUnit\Util\Getopt;
31
use PHPUnit\Util\Log\TeamCity;
32
use PHPUnit\Util\TestDox\TextResultPrinter;
33
use PHPUnit\Util\Printer;
34
use ReflectionClass;
35
use Throwable;
36
37
/**
38
 * A TestRunner for the Command Line Interface (CLI)
39
 * PHP SAPI Module.
40
 */
41
class Command
42
{
43
    /**
44
     * @var array
45
     */
46
    protected $arguments = [
47
        'listGroups'              => false,
48
        'listSuites'              => false,
49
        'loader'                  => null,
50
        'useDefaultConfiguration' => true,
51
        'loadedExtensions'        => [],
52
        'notLoadedExtensions'     => []
53
    ];
54
55
    /**
56
     * @var array
57
     */
58
    protected $options = [];
59
60
    /**
61
     * @var array
62
     */
63
    protected $longOptions = [
64
        'atleast-version='          => null,
65
        'bootstrap='                => null,
66
        'check-version'             => null,
67
        'colors=='                  => null,
68
        'columns='                  => null,
69
        'configuration='            => null,
70
        'coverage-clover='          => null,
71
        'coverage-crap4j='          => null,
72
        'coverage-html='            => null,
73
        'coverage-php='             => null,
74
        'coverage-text=='           => null,
75
        'coverage-xml='             => null,
76
        'debug'                     => null,
77
        'disallow-test-output'      => null,
78
        'disallow-resource-usage'   => null,
79
        'disallow-todo-tests'       => null,
80
        'enforce-time-limit'        => null,
81
        'exclude-group='            => null,
82
        'filter='                   => null,
83
        'generate-configuration'    => null,
84
        'globals-backup'            => null,
85
        'group='                    => null,
86
        'help'                      => null,
87
        'include-path='             => null,
88
        'list-groups'               => null,
89
        'list-suites'               => null,
90
        'loader='                   => null,
91
        'log-junit='                => null,
92
        'log-teamcity='             => null,
93
        'no-configuration'          => null,
94
        'no-coverage'               => null,
95
        'no-extensions'             => null,
96
        'printer='                  => null,
97
        'process-isolation'         => null,
98
        'repeat='                   => null,
99
        'dont-report-useless-tests' => null,
100
        'reverse-list'              => null,
101
        'static-backup'             => null,
102
        'stderr'                    => null,
103
        'stop-on-error'             => null,
104
        'stop-on-failure'           => null,
105
        'stop-on-warning'           => null,
106
        'stop-on-incomplete'        => null,
107
        'stop-on-risky'             => null,
108
        'stop-on-skipped'           => null,
109
        'fail-on-warning'           => null,
110
        'fail-on-risky'             => null,
111
        'strict-coverage'           => null,
112
        'disable-coverage-ignore'   => null,
113
        'strict-global-state'       => null,
114
        'teamcity'                  => null,
115
        'testdox'                   => null,
116
        'testdox-group='            => null,
117
        'testdox-exclude-group='    => null,
118
        'testdox-html='             => null,
119
        'testdox-text='             => null,
120
        'testdox-xml='              => null,
121
        'test-suffix='              => null,
122
        'testsuite='                => null,
123
        'verbose'                   => null,
124
        'version'                   => null,
125
        'whitelist='                => null
126
    ];
127
128
    /**
129
     * @var bool
130
     */
131
    private $versionStringPrinted = false;
132
133
    /**
134
     * @param bool $exit
135
     */
136
    public static function main($exit = true)
137
    {
138
        $command = new static;
139
140
        return $command->run($_SERVER['argv'], $exit);
141
    }
142
143
    /**
144
     * @param array $argv
145
     * @param bool  $exit
146
     *
147
     * @return int
148
     */
149
    public function run(array $argv, $exit = true)
150
    {
151
        $this->handleArguments($argv);
152
153
        $runner = $this->createRunner();
154
155
        if ($this->arguments['test'] instanceof Test) {
156
            $suite = $this->arguments['test'];
157
        } else {
158
            $suite = $runner->getTest(
159
                $this->arguments['test'],
160
                $this->arguments['testFile'],
161
                $this->arguments['testSuffixes']
162
            );
163
        }
164
165
        if ($this->arguments['listGroups']) {
166
            $this->printVersionString();
167
168
            print "Available test group(s):\n";
169
170
            $groups = $suite->getGroups();
171
            \sort($groups);
172
173
            foreach ($groups as $group) {
174
                print " - $group\n";
175
            }
176
177
            if ($exit) {
178
                exit(TestRunner::SUCCESS_EXIT);
179
            }
180
181
            return TestRunner::SUCCESS_EXIT;
182
        }
183
184
        if ($this->arguments['listSuites']) {
185
            $this->printVersionString();
186
187
            print "Available test suite(s):\n";
188
189
            $configuration = Configuration::getInstance(
190
                $this->arguments['configuration']
191
            );
192
193
            $suiteNames = $configuration->getTestSuiteNames();
194
            foreach ($suiteNames as $suiteName) {
195
                print " - $suiteName\n";
196
            }
197
198
            if ($exit) {
199
                exit(TestRunner::SUCCESS_EXIT);
200
            }
201
202
            return TestRunner::SUCCESS_EXIT;
203
        }
204
205
        unset($this->arguments['test']);
206
        unset($this->arguments['testFile']);
207
208
        try {
209
            $result = $runner->doRun($suite, $this->arguments, $exit);
210
        } catch (Exception $e) {
211
            print $e->getMessage() . "\n";
212
        }
213
214
        $return = TestRunner::FAILURE_EXIT;
215
216
        if (isset($result) && $result->wasSuccessful()) {
217
            $return = TestRunner::SUCCESS_EXIT;
218
        } elseif (!isset($result) || $result->errorCount() > 0) {
219
            $return = TestRunner::EXCEPTION_EXIT;
220
        }
221
222
        if ($exit) {
223
            exit($return);
224
        }
225
226
        return $return;
227
    }
228
229
    /**
230
     * Create a TestRunner, override in subclasses.
231
     *
232
     * @return TestRunner
233
     */
234
    protected function createRunner()
235
    {
236
        return new TestRunner($this->arguments['loader']);
237
    }
238
239
    /**
240
     * Handles the command-line arguments.
241
     *
242
     * A child class of PHPUnit_TextUI_Command can hook into the argument
243
     * parsing by adding the switch(es) to the $longOptions array and point to a
244
     * callback method that handles the switch(es) in the child class like this
245
     *
246
     * <code>
247
     * <?php
248
     * class MyCommand extends PHPUnit_TextUI_Command
249
     * {
250
     *     public function __construct()
251
     *     {
252
     *         // my-switch won't accept a value, it's an on/off
253
     *         $this->longOptions['my-switch'] = 'myHandler';
254
     *         // my-secondswitch will accept a value - note the equals sign
255
     *         $this->longOptions['my-secondswitch='] = 'myOtherHandler';
256
     *     }
257
     *
258
     *     // --my-switch  -> myHandler()
259
     *     protected function myHandler()
260
     *     {
261
     *     }
262
     *
263
     *     // --my-secondswitch foo -> myOtherHandler('foo')
264
     *     protected function myOtherHandler ($value)
265
     *     {
266
     *     }
267
     *
268
     *     // You will also need this - the static keyword in the
269
     *     // PHPUnit_TextUI_Command will mean that it'll be
270
     *     // PHPUnit_TextUI_Command that gets instantiated,
271
     *     // not MyCommand
272
     *     public static function main($exit = true)
273
     *     {
274
     *         $command = new static;
275
     *
276
     *         return $command->run($_SERVER['argv'], $exit);
277
     *     }
278
     *
279
     * }
280
     * </code>
281
     *
282
     * @param array $argv
283
     */
284
    protected function handleArguments(array $argv)
285
    {
286
        try {
287
            $this->options = Getopt::getopt(
288
                $argv,
289
                'd:c:hv',
290
                \array_keys($this->longOptions)
291
            );
292
        } catch (Exception $t) {
293
            $this->showError($t->getMessage());
294
        }
295
296
        foreach ($this->options[0] as $option) {
297
            switch ($option[0]) {
298
                case '--colors':
299
                    $this->arguments['colors'] = $option[1] ?: ResultPrinter::COLOR_AUTO;
300
                    break;
301
302
                case '--bootstrap':
303
                    $this->arguments['bootstrap'] = $option[1];
304
                    break;
305
306
                case '--columns':
307
                    if (\is_numeric($option[1])) {
308
                        $this->arguments['columns'] = (int) $option[1];
309
                    } elseif ($option[1] == 'max') {
310
                        $this->arguments['columns'] = 'max';
311
                    }
312
                    break;
313
314
                case 'c':
315
                case '--configuration':
316
                    $this->arguments['configuration'] = $option[1];
317
                    break;
318
319
                case '--coverage-clover':
320
                    $this->arguments['coverageClover'] = $option[1];
321
                    break;
322
323
                case '--coverage-crap4j':
324
                    $this->arguments['coverageCrap4J'] = $option[1];
325
                    break;
326
327
                case '--coverage-html':
328
                    $this->arguments['coverageHtml'] = $option[1];
329
                    break;
330
331
                case '--coverage-php':
332
                    $this->arguments['coveragePHP'] = $option[1];
333
                    break;
334
335
                case '--coverage-text':
336
                    if ($option[1] === null) {
337
                        $option[1] = 'php://stdout';
338
                    }
339
340
                    $this->arguments['coverageText']                   = $option[1];
341
                    $this->arguments['coverageTextShowUncoveredFiles'] = false;
342
                    $this->arguments['coverageTextShowOnlySummary']    = false;
343
                    break;
344
345
                case '--coverage-xml':
346
                    $this->arguments['coverageXml'] = $option[1];
347
                    break;
348
349
                case 'd':
350
                    $ini = \explode('=', $option[1]);
351
352
                    if (isset($ini[0])) {
353
                        if (isset($ini[1])) {
354
                            \ini_set($ini[0], $ini[1]);
355
                        } else {
356
                            \ini_set($ini[0], true);
357
                        }
358
                    }
359
                    break;
360
361
                case '--debug':
362
                    $this->arguments['debug'] = true;
363
                    break;
364
365
                case 'h':
366
                case '--help':
367
                    $this->showHelp();
368
                    exit(TestRunner::SUCCESS_EXIT);
369
                    break;
370
371
                case '--filter':
372
                    $this->arguments['filter'] = $option[1];
373
                    break;
374
375
                case '--testsuite':
376
                    $this->arguments['testsuite'] = $option[1];
377
                    break;
378
379
                case '--generate-configuration':
380
                    $this->printVersionString();
381
382
                    \printf(
383
                        "Generating phpunit.xml in %s\n\n",
384
                        \getcwd()
385
                    );
386
387
                    print 'Bootstrap script (relative to path shown above; default: vendor/autoload.php): ';
388
                    $bootstrapScript = \trim(\fgets(STDIN));
389
390
                    print 'Tests directory (relative to path shown above; default: tests): ';
391
                    $testsDirectory = \trim(\fgets(STDIN));
392
393
                    print 'Source directory (relative to path shown above; default: src): ';
394
                    $src = \trim(\fgets(STDIN));
395
396
                    if ($bootstrapScript == '') {
397
                        $bootstrapScript = 'vendor/autoload.php';
398
                    }
399
400
                    if ($testsDirectory == '') {
401
                        $testsDirectory = 'tests';
402
                    }
403
404
                    if ($src == '') {
405
                        $src = 'src';
406
                    }
407
408
                    $generator = new ConfigurationGenerator;
409
410
                    \file_put_contents(
411
                        'phpunit.xml',
412
                        $generator->generateDefaultConfiguration(
413
                            Version::series(),
414
                            $bootstrapScript,
415
                            $testsDirectory,
416
                            $src
417
                        )
418
                    );
419
420
                    \printf(
421
                        "\nGenerated phpunit.xml in %s\n",
422
                        \getcwd()
423
                    );
424
425
                    exit(TestRunner::SUCCESS_EXIT);
426
                    break;
427
428
                case '--group':
429
                    $this->arguments['groups'] = \explode(',', $option[1]);
430
                    break;
431
432
                case '--exclude-group':
433
                    $this->arguments['excludeGroups'] = \explode(
434
                        ',',
435
                        $option[1]
436
                    );
437
                    break;
438
439
                case '--test-suffix':
440
                    $this->arguments['testSuffixes'] = \explode(
441
                        ',',
442
                        $option[1]
443
                    );
444
                    break;
445
446
                case '--include-path':
447
                    $includePath = $option[1];
448
                    break;
449
450
                case '--list-groups':
451
                    $this->arguments['listGroups'] = true;
452
                    break;
453
454
                case '--list-suites':
455
                    $this->arguments['listSuites'] = true;
456
                    break;
457
458
                case '--printer':
459
                    $this->arguments['printer'] = $option[1];
460
                    break;
461
462
                case '--loader':
463
                    $this->arguments['loader'] = $option[1];
464
                    break;
465
466
                case '--log-junit':
467
                    $this->arguments['junitLogfile'] = $option[1];
468
                    break;
469
470
                case '--log-teamcity':
471
                    $this->arguments['teamcityLogfile'] = $option[1];
472
                    break;
473
474
                case '--process-isolation':
475
                    $this->arguments['processIsolation'] = true;
476
                    break;
477
478
                case '--repeat':
479
                    $this->arguments['repeat'] = (int) $option[1];
480
                    break;
481
482
                case '--stderr':
483
                    $this->arguments['stderr'] = true;
484
                    break;
485
486
                case '--stop-on-error':
487
                    $this->arguments['stopOnError'] = true;
488
                    break;
489
490
                case '--stop-on-failure':
491
                    $this->arguments['stopOnFailure'] = true;
492
                    break;
493
494
                case '--stop-on-warning':
495
                    $this->arguments['stopOnWarning'] = true;
496
                    break;
497
498
                case '--stop-on-incomplete':
499
                    $this->arguments['stopOnIncomplete'] = true;
500
                    break;
501
502
                case '--stop-on-risky':
503
                    $this->arguments['stopOnRisky'] = true;
504
                    break;
505
506
                case '--stop-on-skipped':
507
                    $this->arguments['stopOnSkipped'] = true;
508
                    break;
509
510
                case '--fail-on-warning':
511
                    $this->arguments['failOnWarning'] = true;
512
                    break;
513
514
                case '--fail-on-risky':
515
                    $this->arguments['failOnRisky'] = true;
516
                    break;
517
518
                case '--teamcity':
519
                    $this->arguments['printer'] = TeamCity::class;
520
                    break;
521
522
                case '--testdox':
523
                    $this->arguments['printer'] = TextResultPrinter::class;
524
                    break;
525
526
                case '--testdox-group':
527
                    $this->arguments['testdoxGroups'] = \explode(
528
                        ',',
529
                        $option[1]
530
                    );
531
                    break;
532
533
                case '--testdox-exclude-group':
534
                    $this->arguments['testdoxExcludeGroups'] = \explode(
535
                        ',',
536
                        $option[1]
537
                    );
538
                    break;
539
540
                case '--testdox-html':
541
                    $this->arguments['testdoxHTMLFile'] = $option[1];
542
                    break;
543
544
                case '--testdox-text':
545
                    $this->arguments['testdoxTextFile'] = $option[1];
546
                    break;
547
548
                case '--testdox-xml':
549
                    $this->arguments['testdoxXMLFile'] = $option[1];
550
                    break;
551
552
                case '--no-configuration':
553
                    $this->arguments['useDefaultConfiguration'] = false;
554
                    break;
555
556
                case '--no-extensions':
557
                    $this->arguments['noExtensions'] = true;
558
                    break;
559
560
                case '--no-coverage':
561
                    $this->arguments['noCoverage'] = true;
562
                    break;
563
564
                case '--globals-backup':
565
                    $this->arguments['backupGlobals'] = true;
566
                    break;
567
568
                case '--static-backup':
569
                    $this->arguments['backupStaticAttributes'] = true;
570
                    break;
571
572
                case 'v':
573
                case '--verbose':
574
                    $this->arguments['verbose'] = true;
575
                    break;
576
577
                case '--atleast-version':
578
                    if (\version_compare(Version::id(), $option[1], '>=')) {
579
                        exit(TestRunner::SUCCESS_EXIT);
580
                    }
581
582
                    exit(TestRunner::FAILURE_EXIT);
583
                    break;
584
585
                case '--version':
586
                    $this->printVersionString();
587
                    exit(TestRunner::SUCCESS_EXIT);
588
                    break;
589
590
                case '--dont-report-useless-tests':
591
                    $this->arguments['reportUselessTests'] = false;
592
                    break;
593
594
                case '--strict-coverage':
595
                    $this->arguments['strictCoverage'] = true;
596
                    break;
597
598
                case '--disable-coverage-ignore':
599
                    $this->arguments['disableCodeCoverageIgnore'] = true;
600
                    break;
601
602
                case '--strict-global-state':
603
                    $this->arguments['beStrictAboutChangesToGlobalState'] = true;
604
                    break;
605
606
                case '--disallow-test-output':
607
                    $this->arguments['disallowTestOutput'] = true;
608
                    break;
609
610
                case '--disallow-resource-usage':
611
                    $this->arguments['beStrictAboutResourceUsageDuringSmallTests'] = true;
612
                    break;
613
614
                case '--enforce-time-limit':
615
                    $this->arguments['enforceTimeLimit'] = true;
616
                    break;
617
618
                case '--disallow-todo-tests':
619
                    $this->arguments['disallowTodoAnnotatedTests'] = true;
620
                    break;
621
622
                case '--reverse-list':
623
                    $this->arguments['reverseList'] = true;
624
                    break;
625
626
                case '--check-version':
627
                    $this->handleVersionCheck();
628
                    break;
629
630
                case '--whitelist':
631
                    $this->arguments['whitelist'] = $option[1];
632
                    break;
633
634
                default:
635
                    $optionName = \str_replace('--', '', $option[0]);
636
637
                    $handler = null;
638
                    if (isset($this->longOptions[$optionName])) {
639
                        $handler = $this->longOptions[$optionName];
640
                    } elseif (isset($this->longOptions[$optionName . '='])) {
641
                        $handler = $this->longOptions[$optionName . '='];
642
                    }
643
644
                    if (isset($handler) && \is_callable([$this, $handler])) {
645
                        $this->$handler($option[1]);
646
                    }
647
            }
648
        }
649
650
        $this->handleCustomTestSuite();
651
652
        if (!isset($this->arguments['test'])) {
653
            if (isset($this->options[1][0])) {
654
                $this->arguments['test'] = $this->options[1][0];
655
            }
656
657
            if (isset($this->options[1][1])) {
658
                $this->arguments['testFile'] = \realpath($this->options[1][1]);
659
            } else {
660
                $this->arguments['testFile'] = '';
661
            }
662
663
            if (isset($this->arguments['test']) &&
664
                \is_file($this->arguments['test']) &&
665
                \substr($this->arguments['test'], -5, 5) != '.phpt') {
666
                $this->arguments['testFile'] = \realpath($this->arguments['test']);
667
                $this->arguments['test']     = \substr($this->arguments['test'], 0, \strrpos($this->arguments['test'], '.'));
668
            }
669
        }
670
671
        if (!isset($this->arguments['testSuffixes'])) {
672
            $this->arguments['testSuffixes'] = ['Test.php', '.phpt'];
673
        }
674
675
        if (isset($includePath)) {
676
            \ini_set(
677
                'include_path',
678
                $includePath . PATH_SEPARATOR . \ini_get('include_path')
679
            );
680
        }
681
682
        if ($this->arguments['loader'] !== null) {
683
            $this->arguments['loader'] = $this->handleLoader($this->arguments['loader']);
684
        }
685
686
        if (isset($this->arguments['configuration']) &&
687
            \is_dir($this->arguments['configuration'])) {
688
            $configurationFile = $this->arguments['configuration'] . '/phpunit.xml';
689
690
            if (\file_exists($configurationFile)) {
691
                $this->arguments['configuration'] = \realpath(
692
                    $configurationFile
693
                );
694
            } elseif (\file_exists($configurationFile . '.dist')) {
695
                $this->arguments['configuration'] = \realpath(
696
                    $configurationFile . '.dist'
697
                );
698
            }
699
        } elseif (!isset($this->arguments['configuration']) &&
700
            $this->arguments['useDefaultConfiguration']) {
701
            if (\file_exists('phpunit.xml')) {
702
                $this->arguments['configuration'] = \realpath('phpunit.xml');
703
            } elseif (\file_exists('phpunit.xml.dist')) {
704
                $this->arguments['configuration'] = \realpath(
705
                    'phpunit.xml.dist'
706
                );
707
            }
708
        }
709
710
        if (isset($this->arguments['configuration'])) {
711
            try {
712
                $configuration = Configuration::getInstance(
713
                    $this->arguments['configuration']
714
                );
715
            } catch (Throwable $t) {
716
                print $t->getMessage() . "\n";
717
                exit(TestRunner::FAILURE_EXIT);
718
            }
719
720
            $phpunitConfiguration = $configuration->getPHPUnitConfiguration();
721
722
            $configuration->handlePHPConfiguration();
723
724
            /*
725
             * Issue #1216
726
             */
727
            if (isset($this->arguments['bootstrap'])) {
728
                $this->handleBootstrap($this->arguments['bootstrap']);
729
            } elseif (isset($phpunitConfiguration['bootstrap'])) {
730
                $this->handleBootstrap($phpunitConfiguration['bootstrap']);
731
            }
732
733
            /*
734
             * Issue #657
735
             */
736
            if (isset($phpunitConfiguration['stderr']) && !isset($this->arguments['stderr'])) {
737
                $this->arguments['stderr'] = $phpunitConfiguration['stderr'];
738
            }
739
740
            if (isset($phpunitConfiguration['extensionsDirectory']) && !isset($this->arguments['noExtensions']) && \extension_loaded('phar')) {
741
                $this->handleExtensions($phpunitConfiguration['extensionsDirectory']);
742
            }
743
744
            if (isset($phpunitConfiguration['columns']) && !isset($this->arguments['columns'])) {
745
                $this->arguments['columns'] = $phpunitConfiguration['columns'];
746
            }
747
748
            if (!isset($this->arguments['printer']) && isset($phpunitConfiguration['printerClass'])) {
749
                if (isset($phpunitConfiguration['printerFile'])) {
750
                    $file = $phpunitConfiguration['printerFile'];
751
                } else {
752
                    $file = '';
753
                }
754
755
                $this->arguments['printer'] = $this->handlePrinter(
756
                    $phpunitConfiguration['printerClass'],
757
                    $file
758
                );
759
            }
760
761
            if (isset($phpunitConfiguration['testSuiteLoaderClass'])) {
762
                if (isset($phpunitConfiguration['testSuiteLoaderFile'])) {
763
                    $file = $phpunitConfiguration['testSuiteLoaderFile'];
764
                } else {
765
                    $file = '';
766
                }
767
768
                $this->arguments['loader'] = $this->handleLoader(
769
                    $phpunitConfiguration['testSuiteLoaderClass'],
770
                    $file
771
                );
772
            }
773
774
            if (!isset($this->arguments['testsuite']) && isset($phpunitConfiguration['defaultTestSuite'])) {
775
                $this->arguments['testsuite'] = $phpunitConfiguration['defaultTestSuite'];
776
            }
777
778
            if (!isset($this->arguments['test'])) {
779
                $testSuite = $configuration->getTestSuiteConfiguration($this->arguments['testsuite'] ?? null);
780
781
                if ($testSuite !== null) {
782
                    $this->arguments['test'] = $testSuite;
783
                }
784
            }
785
        } elseif (isset($this->arguments['bootstrap'])) {
786
            $this->handleBootstrap($this->arguments['bootstrap']);
787
        }
788
789
        if (isset($this->arguments['printer']) &&
790
            \is_string($this->arguments['printer'])) {
791
            $this->arguments['printer'] = $this->handlePrinter($this->arguments['printer']);
792
        }
793
794
        if (isset($this->arguments['test']) && \is_string($this->arguments['test']) && \substr($this->arguments['test'], -5, 5) == '.phpt') {
795
            $test = new PhptTestCase($this->arguments['test']);
796
797
            $this->arguments['test'] = new TestSuite;
798
            $this->arguments['test']->addTest($test);
799
        }
800
801
        if (!isset($this->arguments['test'])) {
802
            $this->showHelp();
803
            exit(TestRunner::EXCEPTION_EXIT);
804
        }
805
    }
806
807
    /**
808
     * Handles the loading of the PHPUnit_Runner_TestSuiteLoader implementation.
809
     *
810
     * @param string $loaderClass
811
     * @param string $loaderFile
812
     *
813
     * @return TestSuiteLoader
814
     */
815
    protected function handleLoader($loaderClass, $loaderFile = '')
816
    {
817
        if (!\class_exists($loaderClass, false)) {
818
            if ($loaderFile == '') {
819
                $loaderFile = Filesystem::classNameToFilename(
820
                    $loaderClass
821
                );
822
            }
823
824
            $loaderFile = \stream_resolve_include_path($loaderFile);
825
826
            if ($loaderFile) {
827
                require $loaderFile;
828
            }
829
        }
830
831
        if (\class_exists($loaderClass, false)) {
832
            $class = new ReflectionClass($loaderClass);
833
834
            if ($class->implementsInterface(TestSuiteLoader::class) &&
835
                $class->isInstantiable()) {
836
                return $class->newInstance();
837
            }
838
        }
839
840
        if ($loaderClass == StandardTestSuiteLoader::class) {
841
            return;
842
        }
843
844
        $this->showError(
845
            \sprintf(
846
                'Could not use "%s" as loader.',
847
                $loaderClass
848
            )
849
        );
850
    }
851
852
    /**
853
     * Handles the loading of the PHPUnit_Util_Printer implementation.
854
     *
855
     * @param string $printerClass
856
     * @param string $printerFile
857
     *
858
     * @return Printer|string
859
     */
860
    protected function handlePrinter($printerClass, $printerFile = '')
861
    {
862
        if (!\class_exists($printerClass, false)) {
863
            if ($printerFile == '') {
864
                $printerFile = Filesystem::classNameToFilename(
865
                    $printerClass
866
                );
867
            }
868
869
            $printerFile = \stream_resolve_include_path($printerFile);
870
871
            if ($printerFile) {
872
                require $printerFile;
873
            }
874
        }
875
876
        if (\class_exists($printerClass)) {
877
            $class = new ReflectionClass($printerClass);
878
879
            if ($class->implementsInterface(TestListener::class) &&
880
                $class->isSubclassOf(Printer::class) &&
881
                $class->isInstantiable()) {
882
                if ($class->isSubclassOf(ResultPrinter::class)) {
883
                    return $printerClass;
884
                }
885
886
                $outputStream = isset($this->arguments['stderr']) ? 'php://stderr' : null;
887
888
                return $class->newInstance($outputStream);
889
            }
890
        }
891
892
        $this->showError(
893
            \sprintf(
894
                'Could not use "%s" as printer.',
895
                $printerClass
896
            )
897
        );
898
    }
899
900
    /**
901
     * Loads a bootstrap file.
902
     *
903
     * @param string $filename
904
     */
905
    protected function handleBootstrap($filename)
906
    {
907
        try {
908
            Fileloader::checkAndLoad($filename);
909
        } catch (Exception $e) {
910
            $this->showError($e->getMessage());
911
        }
912
    }
913
914
    protected function handleVersionCheck()
915
    {
916
        $this->printVersionString();
917
918
        $latestVersion = \file_get_contents('https://phar.phpunit.de/latest-version-of/phpunit');
919
        $isOutdated    = \version_compare($latestVersion, Version::id(), '>');
920
921
        if ($isOutdated) {
922
            \printf(
923
                "You are not using the latest version of PHPUnit.\n" .
924
                "The latest version is PHPUnit %s.\n",
925
                $latestVersion
926
            );
927
        } else {
928
            print "You are using the latest version of PHPUnit.\n";
929
        }
930
931
        exit(TestRunner::SUCCESS_EXIT);
932
    }
933
934
    /**
935
     * Show the help message.
936
     */
937
    protected function showHelp()
938
    {
939
        $this->printVersionString();
940
941
        print <<<EOT
942
Usage: phpunit [options] UnitTest [UnitTest.php]
943
       phpunit [options] <directory>
944
945
Code Coverage Options:
946
947
  --coverage-clover <file>    Generate code coverage report in Clover XML format.
948
  --coverage-crap4j <file>    Generate code coverage report in Crap4J XML format.
949
  --coverage-html <dir>       Generate code coverage report in HTML format.
950
  --coverage-php <file>       Export PHP_CodeCoverage object to file.
951
  --coverage-text=<file>      Generate code coverage report in text format.
952
                              Default: Standard output.
953
  --coverage-xml <dir>        Generate code coverage report in PHPUnit XML format.
954
  --whitelist <dir>           Whitelist <dir> for code coverage analysis.
955
  --disable-coverage-ignore   Disable annotations for ignoring code coverage.
956
957
Logging Options:
958
959
  --log-junit <file>          Log test execution in JUnit XML format to file.
960
  --log-teamcity <file>       Log test execution in TeamCity format to file.
961
  --testdox-html <file>       Write agile documentation in HTML format to file.
962
  --testdox-text <file>       Write agile documentation in Text format to file.
963
  --testdox-xml <file>        Write agile documentation in XML format to file.
964
  --reverse-list              Print defects in reverse order
965
966
Test Selection Options:
967
968
  --filter <pattern>          Filter which tests to run.
969
  --testsuite <name,...>      Filter which testsuite to run.
970
  --group ...                 Only runs tests from the specified group(s).
971
  --exclude-group ...         Exclude tests from the specified group(s).
972
  --list-groups               List available test groups.
973
  --list-suites               List available test suites.
974
  --test-suffix ...           Only search for test in files with specified
975
                              suffix(es). Default: Test.php,.phpt
976
977
Test Execution Options:
978
979
  --dont-report-useless-tests Do not report tests that do not test anything.
980
  --strict-coverage           Be strict about @covers annotation usage.
981
  --strict-global-state       Be strict about changes to global state
982
  --disallow-test-output      Be strict about output during tests.
983
  --disallow-resource-usage   Be strict about resource usage during small tests.
984
  --enforce-time-limit        Enforce time limit based on test size.
985
  --disallow-todo-tests       Disallow @todo-annotated tests.
986
987
  --process-isolation         Run each test in a separate PHP process.
988
  --globals-backup            Backup and restore \$GLOBALS for each test.
989
  --static-backup             Backup and restore static attributes for each test.
990
991
  --colors=<flag>             Use colors in output ("never", "auto" or "always").
992
  --columns <n>               Number of columns to use for progress output.
993
  --columns max               Use maximum number of columns for progress output.
994
  --stderr                    Write to STDERR instead of STDOUT.
995
  --stop-on-error             Stop execution upon first error.
996
  --stop-on-failure           Stop execution upon first error or failure.
997
  --stop-on-warning           Stop execution upon first warning.
998
  --stop-on-risky             Stop execution upon first risky test.
999
  --stop-on-skipped           Stop execution upon first skipped test.
1000
  --stop-on-incomplete        Stop execution upon first incomplete test.
1001
  --fail-on-warning           Treat tests with warnings as failures.
1002
  --fail-on-risky             Treat risky tests as failures.
1003
  -v|--verbose                Output more verbose information.
1004
  --debug                     Display debugging information.
1005
1006
  --loader <loader>           TestSuiteLoader implementation to use.
1007
  --repeat <times>            Runs the test(s) repeatedly.
1008
  --teamcity                  Report test execution progress in TeamCity format.
1009
  --testdox                   Report test execution progress in TestDox format.
1010
  --testdox-group             Only include tests from the specified group(s).
1011
  --testdox-exclude-group     Exclude tests from the specified group(s).
1012
  --printer <printer>         TestListener implementation to use.
1013
1014
Configuration Options:
1015
1016
  --bootstrap <file>          A "bootstrap" PHP file that is run before the tests.
1017
  -c|--configuration <file>   Read configuration from XML file.
1018
  --no-configuration          Ignore default configuration file (phpunit.xml).
1019
  --no-coverage               Ignore code coverage configuration.
1020
  --no-extensions             Do not load PHPUnit extensions.
1021
  --include-path <path(s)>    Prepend PHP's include_path with given path(s).
1022
  -d key[=value]              Sets a php.ini value.
1023
  --generate-configuration    Generate configuration file with suggested settings.
1024
1025
Miscellaneous Options:
1026
1027
  -h|--help                   Prints this usage information.
1028
  --version                   Prints the version and exits.
1029
  --atleast-version <min>     Checks that version is greater than min and exits.
1030
  --check-version             Check whether PHPUnit is the latest version.
1031
1032
EOT;
1033
    }
1034
1035
    /**
1036
     * Custom callback for test suite discovery.
1037
     */
1038
    protected function handleCustomTestSuite()
1039
    {
1040
    }
1041
1042
    private function printVersionString()
1043
    {
1044
        if ($this->versionStringPrinted) {
1045
            return;
1046
        }
1047
1048
        print Version::getVersionString() . "\n\n";
1049
1050
        $this->versionStringPrinted = true;
1051
    }
1052
1053
    /**
1054
     * @param string $message
1055
     */
1056
    private function showError($message)
1057
    {
1058
        $this->printVersionString();
1059
1060
        print $message . "\n";
1061
1062
        exit(TestRunner::FAILURE_EXIT);
1063
    }
1064
1065
    /**
1066
     * @param string $directory
1067
     */
1068
    private function handleExtensions($directory)
1069
    {
1070
        $facade = new File_Iterator_Facade;
1071
1072
        foreach ($facade->getFilesAsArray($directory, '.phar') as $file) {
1073
            if (!\file_exists('phar://' . $file . '/manifest.xml')) {
1074
                $this->arguments['notLoadedExtensions'][] = $file . ' is not an extension for PHPUnit';
1075
1076
                continue;
1077
            }
1078
1079
            try {
1080
                $applicationName = new ApplicationName('phpunit/phpunit');
1081
                $version         = new PharIoVersion(Version::series());
1082
                $manifest        = ManifestLoader::fromFile('phar://' . $file . '/manifest.xml');
1083
1084
                if (!$manifest->isExtensionFor($applicationName)) {
1085
                    $this->arguments['notLoadedExtensions'][] = $file . ' is not an extension for PHPUnit';
1086
1087
                    continue;
1088
                }
1089
1090
                if (!$manifest->isExtensionFor($applicationName, $version)) {
1091
                    $this->arguments['notLoadedExtensions'][] = $file . ' is not compatible with this version of PHPUnit';
1092
1093
                    continue;
1094
                }
1095
            } catch (ManifestException $e) {
1096
                $this->arguments['notLoadedExtensions'][] = $file . ': ' . $e->getMessage();
1097
1098
                continue;
1099
            }
1100
1101
            require $file;
1102
1103
            $this->arguments['loadedExtensions'][] = $manifest->getName() . ' ' . $manifest->getVersion()->getVersionString();
1104
        }
1105
    }
1106
}
1107