Command::handleOrderByOption()   B
last analyzed

Complexity

Conditions 7
Paths 7

Size

Total Lines 33
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 21
nc 7
nop 1
dl 0
loc 33
rs 8.6506
c 0
b 0
f 0
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
namespace PHPUnit\TextUI;
11
12
use PharIo\Manifest\ApplicationName;
13
use PharIo\Manifest\Exception as ManifestException;
14
use PharIo\Manifest\ManifestLoader;
15
use PharIo\Version\Version as PharIoVersion;
16
use PHPUnit\Framework\Exception;
17
use PHPUnit\Framework\Test;
18
use PHPUnit\Framework\TestListener;
19
use PHPUnit\Framework\TestSuite;
20
use PHPUnit\Runner\PhptTestCase;
21
use PHPUnit\Runner\StandardTestSuiteLoader;
22
use PHPUnit\Runner\TestSuiteLoader;
23
use PHPUnit\Runner\TestSuiteSorter;
24
use PHPUnit\Runner\Version;
25
use PHPUnit\Util\Configuration;
26
use PHPUnit\Util\ConfigurationGenerator;
27
use PHPUnit\Util\FileLoader;
28
use PHPUnit\Util\Filesystem;
29
use PHPUnit\Util\Getopt;
30
use PHPUnit\Util\Log\TeamCity;
31
use PHPUnit\Util\Printer;
32
use PHPUnit\Util\TestDox\CliTestDoxPrinter;
33
use PHPUnit\Util\TextTestListRenderer;
34
use PHPUnit\Util\XmlTestListRenderer;
35
use ReflectionClass;
36
use SebastianBergmann\FileIterator\Facade as FileIteratorFacade;
37
38
use Throwable;
39
40
/**
41
 * A TestRunner for the Command Line Interface (CLI)
42
 * PHP SAPI Module.
43
 */
44
class Command
45
{
46
    /**
47
     * @var array
48
     */
49
    protected $arguments = [
50
        'listGroups'              => false,
51
        'listSuites'              => false,
52
        'listTests'               => false,
53
        'listTestsXml'            => false,
54
        'loader'                  => null,
55
        'useDefaultConfiguration' => true,
56
        'loadedExtensions'        => [],
57
        'notLoadedExtensions'     => [],
58
    ];
59
60
    /**
61
     * @var array
62
     */
63
    protected $options = [];
64
65
    /**
66
     * @var array
67
     */
68
    protected $longOptions = [
69
        'atleast-version='          => null,
70
        'prepend='                  => null,
71
        'bootstrap='                => null,
72
        'cache-result'              => null,
73
        'cache-result-file='        => null,
74
        'check-version'             => null,
75
        'colors=='                  => null,
76
        'columns='                  => null,
77
        'configuration='            => null,
78
        'coverage-clover='          => null,
79
        'coverage-crap4j='          => null,
80
        'coverage-html='            => null,
81
        'coverage-php='             => null,
82
        'coverage-text=='           => null,
83
        'coverage-xml='             => null,
84
        'debug'                     => null,
85
        'disallow-test-output'      => null,
86
        'disallow-resource-usage'   => null,
87
        'disallow-todo-tests'       => null,
88
        'default-time-limit='       => null,
89
        'enforce-time-limit'        => null,
90
        'exclude-group='            => null,
91
        'filter='                   => null,
92
        'generate-configuration'    => null,
93
        'globals-backup'            => null,
94
        'group='                    => null,
95
        'help'                      => null,
96
        'resolve-dependencies'      => null,
97
        'ignore-dependencies'       => null,
98
        'include-path='             => null,
99
        'list-groups'               => null,
100
        'list-suites'               => null,
101
        'list-tests'                => null,
102
        'list-tests-xml='           => null,
103
        'loader='                   => null,
104
        'log-junit='                => null,
105
        'log-teamcity='             => null,
106
        'no-configuration'          => null,
107
        'no-coverage'               => null,
108
        'no-logging'                => null,
109
        'no-extensions'             => null,
110
        'order-by='                 => null,
111
        'printer='                  => null,
112
        'process-isolation'         => null,
113
        'repeat='                   => null,
114
        'dont-report-useless-tests' => null,
115
        'random-order'              => null,
116
        'random-order-seed='        => null,
117
        'reverse-order'             => null,
118
        'reverse-list'              => null,
119
        'static-backup'             => null,
120
        'stderr'                    => null,
121
        'stop-on-defect'            => null,
122
        'stop-on-error'             => null,
123
        'stop-on-failure'           => null,
124
        'stop-on-warning'           => null,
125
        'stop-on-incomplete'        => null,
126
        'stop-on-risky'             => null,
127
        'stop-on-skipped'           => null,
128
        'fail-on-warning'           => null,
129
        'fail-on-risky'             => null,
130
        'strict-coverage'           => null,
131
        'disable-coverage-ignore'   => null,
132
        'strict-global-state'       => null,
133
        'teamcity'                  => null,
134
        'testdox'                   => null,
135
        'testdox-group='            => null,
136
        'testdox-exclude-group='    => null,
137
        'testdox-html='             => null,
138
        'testdox-text='             => null,
139
        'testdox-xml='              => null,
140
        'test-suffix='              => null,
141
        'testsuite='                => null,
142
        'verbose'                   => null,
143
        'version'                   => null,
144
        'whitelist='                => null,
145
        'dump-xdebug-filter='       => null,
146
    ];
147
148
    /**
149
     * @var bool
150
     */
151
    private $versionStringPrinted = false;
152
153
    /**
154
     * @throws \RuntimeException
155
     * @throws \PHPUnit\Framework\Exception
156
     * @throws \InvalidArgumentException
157
     */
158
    public static function main(bool $exit = true): int
159
    {
160
        $command = new static;
161
162
        return $command->run($_SERVER['argv'], $exit);
163
    }
164
165
    /**
166
     * @throws \RuntimeException
167
     * @throws \ReflectionException
168
     * @throws \InvalidArgumentException
169
     * @throws Exception
170
     */
171
    public function run(array $argv, bool $exit = true): int
172
    {
173
        $this->handleArguments($argv);
174
175
        $runner = $this->createRunner();
176
177
        if ($this->arguments['test'] instanceof Test) {
178
            $suite = $this->arguments['test'];
179
        } else {
180
            $suite = $runner->getTest(
181
                $this->arguments['test'],
182
                $this->arguments['testFile'],
183
                $this->arguments['testSuffixes']
184
            );
185
        }
186
187
        if ($this->arguments['listGroups']) {
188
            return $this->handleListGroups($suite, $exit);
189
        }
190
191
        if ($this->arguments['listSuites']) {
192
            return $this->handleListSuites($exit);
193
        }
194
195
        if ($this->arguments['listTests']) {
196
            return $this->handleListTests($suite, $exit);
197
        }
198
199
        if ($this->arguments['listTestsXml']) {
200
            return $this->handleListTestsXml($suite, $this->arguments['listTestsXml'], $exit);
201
        }
202
203
        unset($this->arguments['test'], $this->arguments['testFile']);
204
205
        try {
206
            $result = $runner->doRun($suite, $this->arguments, $exit);
207
        } catch (Exception $e) {
208
            print $e->getMessage() . \PHP_EOL;
209
        }
210
211
        $return = TestRunner::FAILURE_EXIT;
212
213
        if (isset($result) && $result->wasSuccessful()) {
214
            $return = TestRunner::SUCCESS_EXIT;
215
        } elseif (!isset($result) || $result->errorCount() > 0) {
216
            $return = TestRunner::EXCEPTION_EXIT;
217
        }
218
219
        if ($exit) {
220
            exit($return);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return integer. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
221
        }
222
223
        return $return;
224
    }
225
226
    /**
227
     * Create a TestRunner, override in subclasses.
228
     */
229
    protected function createRunner(): TestRunner
230
    {
231
        return new TestRunner($this->arguments['loader']);
232
    }
233
234
    /**
235
     * Handles the command-line arguments.
236
     *
237
     * A child class of PHPUnit\TextUI\Command can hook into the argument
238
     * parsing by adding the switch(es) to the $longOptions array and point to a
239
     * callback method that handles the switch(es) in the child class like this
240
     *
241
     * <code>
242
     * <?php
243
     * class MyCommand extends PHPUnit\TextUI\Command
244
     * {
245
     *     public function __construct()
246
     *     {
247
     *         // my-switch won't accept a value, it's an on/off
248
     *         $this->longOptions['my-switch'] = 'myHandler';
249
     *         // my-secondswitch will accept a value - note the equals sign
250
     *         $this->longOptions['my-secondswitch='] = 'myOtherHandler';
251
     *     }
252
     *
253
     *     // --my-switch  -> myHandler()
254
     *     protected function myHandler()
255
     *     {
256
     *     }
257
     *
258
     *     // --my-secondswitch foo -> myOtherHandler('foo')
259
     *     protected function myOtherHandler ($value)
260
     *     {
261
     *     }
262
     *
263
     *     // You will also need this - the static keyword in the
264
     *     // PHPUnit\TextUI\Command will mean that it'll be
265
     *     // PHPUnit\TextUI\Command that gets instantiated,
266
     *     // not MyCommand
267
     *     public static function main($exit = true)
268
     *     {
269
     *         $command = new static;
270
     *
271
     *         return $command->run($_SERVER['argv'], $exit);
272
     *     }
273
     *
274
     * }
275
     * </code>
276
     *
277
     * @throws Exception
278
     */
279
    protected function handleArguments(array $argv): void
280
    {
281
        try {
282
            $this->options = Getopt::getopt(
283
                $argv,
284
                'd:c:hv',
285
                \array_keys($this->longOptions)
286
            );
287
        } catch (Exception $t) {
288
            $this->exitWithErrorMessage($t->getMessage());
289
        }
290
291
        foreach ($this->options[0] as $option) {
292
            switch ($option[0]) {
293
                case '--colors':
294
                    $this->arguments['colors'] = $option[1] ?: ResultPrinter::COLOR_AUTO;
295
296
                    break;
297
298
                case '--bootstrap':
299
                    $this->arguments['bootstrap'] = $option[1];
300
301
                    break;
302
303
                case '--cache-result':
304
                    $this->arguments['cacheResult'] = true;
305
306
                    break;
307
308
                case '--cache-result-file':
309
                    $this->arguments['cacheResultFile'] = $option[1];
310
311
                    break;
312
313
                case '--columns':
314
                    if (\is_numeric($option[1])) {
315
                        $this->arguments['columns'] = (int) $option[1];
316
                    } elseif ($option[1] === 'max') {
317
                        $this->arguments['columns'] = 'max';
318
                    }
319
320
                    break;
321
322
                case 'c':
323
                case '--configuration':
324
                    $this->arguments['configuration'] = $option[1];
325
326
                    break;
327
328
                case '--coverage-clover':
329
                    $this->arguments['coverageClover'] = $option[1];
330
331
                    break;
332
333
                case '--coverage-crap4j':
334
                    $this->arguments['coverageCrap4J'] = $option[1];
335
336
                    break;
337
338
                case '--coverage-html':
339
                    $this->arguments['coverageHtml'] = $option[1];
340
341
                    break;
342
343
                case '--coverage-php':
344
                    $this->arguments['coveragePHP'] = $option[1];
345
346
                    break;
347
348
                case '--coverage-text':
349
                    if ($option[1] === null) {
350
                        $option[1] = 'php://stdout';
351
                    }
352
353
                    $this->arguments['coverageText']                   = $option[1];
354
                    $this->arguments['coverageTextShowUncoveredFiles'] = false;
355
                    $this->arguments['coverageTextShowOnlySummary']    = false;
356
357
                    break;
358
359
                case '--coverage-xml':
360
                    $this->arguments['coverageXml'] = $option[1];
361
362
                    break;
363
364
                case 'd':
365
                    $ini = \explode('=', $option[1]);
366
367
                    if (isset($ini[0])) {
368
                        if (isset($ini[1])) {
369
                            \ini_set($ini[0], $ini[1]);
370
                        } else {
371
                            \ini_set($ini[0], true);
0 ignored issues
show
Bug introduced by
true of type true is incompatible with the type string expected by parameter $value of ini_set(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

371
                            \ini_set($ini[0], /** @scrutinizer ignore-type */ true);
Loading history...
372
                        }
373
                    }
374
375
                    break;
376
377
                case '--debug':
378
                    $this->arguments['debug'] = true;
379
380
                    break;
381
382
                case 'h':
383
                case '--help':
384
                    $this->showHelp();
385
                    exit(TestRunner::SUCCESS_EXIT);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
386
387
                    break;
388
389
                case '--filter':
390
                    $this->arguments['filter'] = $option[1];
391
392
                    break;
393
394
                case '--testsuite':
395
                    $this->arguments['testsuite'] = $option[1];
396
397
                    break;
398
399
                case '--generate-configuration':
400
                    $this->printVersionString();
401
402
                    print 'Generating phpunit.xml in ' . \getcwd() . \PHP_EOL . \PHP_EOL;
403
404
                    print 'Bootstrap script (relative to path shown above; default: vendor/autoload.php): ';
405
                    $bootstrapScript = \trim(\fgets(\STDIN));
406
407
                    print 'Tests directory (relative to path shown above; default: tests): ';
408
                    $testsDirectory = \trim(\fgets(\STDIN));
409
410
                    print 'Source directory (relative to path shown above; default: src): ';
411
                    $src = \trim(\fgets(\STDIN));
412
413
                    if ($bootstrapScript === '') {
414
                        $bootstrapScript = 'vendor/autoload.php';
415
                    }
416
417
                    if ($testsDirectory === '') {
418
                        $testsDirectory = 'tests';
419
                    }
420
421
                    if ($src === '') {
422
                        $src = 'src';
423
                    }
424
425
                    $generator = new ConfigurationGenerator;
426
427
                    \file_put_contents(
428
                        'phpunit.xml',
429
                        $generator->generateDefaultConfiguration(
430
                            Version::series(),
431
                            $bootstrapScript,
432
                            $testsDirectory,
433
                            $src
434
                        )
435
                    );
436
437
                    print \PHP_EOL . 'Generated phpunit.xml in ' . \getcwd() . \PHP_EOL;
438
439
                    exit(TestRunner::SUCCESS_EXIT);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
440
441
                    break;
442
443
                case '--group':
444
                    $this->arguments['groups'] = \explode(',', $option[1]);
445
446
                    break;
447
448
                case '--exclude-group':
449
                    $this->arguments['excludeGroups'] = \explode(
450
                        ',',
451
                        $option[1]
452
                    );
453
454
                    break;
455
456
                case '--test-suffix':
457
                    $this->arguments['testSuffixes'] = \explode(
458
                        ',',
459
                        $option[1]
460
                    );
461
462
                    break;
463
464
                case '--include-path':
465
                    $includePath = $option[1];
466
467
                    break;
468
469
                case '--list-groups':
470
                    $this->arguments['listGroups'] = true;
471
472
                    break;
473
474
                case '--list-suites':
475
                    $this->arguments['listSuites'] = true;
476
477
                    break;
478
479
                case '--list-tests':
480
                    $this->arguments['listTests'] = true;
481
482
                    break;
483
484
                case '--list-tests-xml':
485
                    $this->arguments['listTestsXml'] = $option[1];
486
487
                    break;
488
489
                case '--printer':
490
                    $this->arguments['printer'] = $option[1];
491
492
                    break;
493
494
                case '--loader':
495
                    $this->arguments['loader'] = $option[1];
496
497
                    break;
498
499
                case '--log-junit':
500
                    $this->arguments['junitLogfile'] = $option[1];
501
502
                    break;
503
504
                case '--log-teamcity':
505
                    $this->arguments['teamcityLogfile'] = $option[1];
506
507
                    break;
508
509
                case '--order-by':
510
                    $this->handleOrderByOption($option[1]);
511
512
                    break;
513
514
                case '--process-isolation':
515
                    $this->arguments['processIsolation'] = true;
516
517
                    break;
518
519
                case '--repeat':
520
                    $this->arguments['repeat'] = (int) $option[1];
521
522
                    break;
523
524
                case '--stderr':
525
                    $this->arguments['stderr'] = true;
526
527
                    break;
528
529
                case '--stop-on-defect':
530
                    $this->arguments['stopOnDefect'] = true;
531
532
                    break;
533
534
                case '--stop-on-error':
535
                    $this->arguments['stopOnError'] = true;
536
537
                    break;
538
539
                case '--stop-on-failure':
540
                    $this->arguments['stopOnFailure'] = true;
541
542
                    break;
543
544
                case '--stop-on-warning':
545
                    $this->arguments['stopOnWarning'] = true;
546
547
                    break;
548
549
                case '--stop-on-incomplete':
550
                    $this->arguments['stopOnIncomplete'] = true;
551
552
                    break;
553
554
                case '--stop-on-risky':
555
                    $this->arguments['stopOnRisky'] = true;
556
557
                    break;
558
559
                case '--stop-on-skipped':
560
                    $this->arguments['stopOnSkipped'] = true;
561
562
                    break;
563
564
                case '--fail-on-warning':
565
                    $this->arguments['failOnWarning'] = true;
566
567
                    break;
568
569
                case '--fail-on-risky':
570
                    $this->arguments['failOnRisky'] = true;
571
572
                    break;
573
574
                case '--teamcity':
575
                    $this->arguments['printer'] = TeamCity::class;
576
577
                    break;
578
579
                case '--testdox':
580
                    $this->arguments['printer'] = CliTestDoxPrinter::class;
581
582
                    break;
583
584
                case '--testdox-group':
585
                    $this->arguments['testdoxGroups'] = \explode(
586
                        ',',
587
                        $option[1]
588
                    );
589
590
                    break;
591
592
                case '--testdox-exclude-group':
593
                    $this->arguments['testdoxExcludeGroups'] = \explode(
594
                        ',',
595
                        $option[1]
596
                    );
597
598
                    break;
599
600
                case '--testdox-html':
601
                    $this->arguments['testdoxHTMLFile'] = $option[1];
602
603
                    break;
604
605
                case '--testdox-text':
606
                    $this->arguments['testdoxTextFile'] = $option[1];
607
608
                    break;
609
610
                case '--testdox-xml':
611
                    $this->arguments['testdoxXMLFile'] = $option[1];
612
613
                    break;
614
615
                case '--no-configuration':
616
                    $this->arguments['useDefaultConfiguration'] = false;
617
618
                    break;
619
620
                case '--no-extensions':
621
                    $this->arguments['noExtensions'] = true;
622
623
                    break;
624
625
                case '--no-coverage':
626
                    $this->arguments['noCoverage'] = true;
627
628
                    break;
629
630
                case '--no-logging':
631
                    $this->arguments['noLogging'] = true;
632
633
                    break;
634
635
                case '--globals-backup':
636
                    $this->arguments['backupGlobals'] = true;
637
638
                    break;
639
640
                case '--static-backup':
641
                    $this->arguments['backupStaticAttributes'] = true;
642
643
                    break;
644
645
                case 'v':
646
                case '--verbose':
647
                    $this->arguments['verbose'] = true;
648
649
                    break;
650
651
                case '--atleast-version':
652
                    if (\version_compare(Version::id(), $option[1], '>=')) {
653
                        exit(TestRunner::SUCCESS_EXIT);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
654
                    }
655
656
                    exit(TestRunner::FAILURE_EXIT);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
657
658
                    break;
659
660
                case '--version':
661
                    $this->printVersionString();
662
                    exit(TestRunner::SUCCESS_EXIT);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
663
664
                    break;
665
666
                case '--dont-report-useless-tests':
667
                    $this->arguments['reportUselessTests'] = false;
668
669
                    break;
670
671
                case '--strict-coverage':
672
                    $this->arguments['strictCoverage'] = true;
673
674
                    break;
675
676
                case '--disable-coverage-ignore':
677
                    $this->arguments['disableCodeCoverageIgnore'] = true;
678
679
                    break;
680
681
                case '--strict-global-state':
682
                    $this->arguments['beStrictAboutChangesToGlobalState'] = true;
683
684
                    break;
685
686
                case '--disallow-test-output':
687
                    $this->arguments['disallowTestOutput'] = true;
688
689
                    break;
690
691
                case '--disallow-resource-usage':
692
                    $this->arguments['beStrictAboutResourceUsageDuringSmallTests'] = true;
693
694
                    break;
695
696
                case '--default-time-limit':
697
                    $this->arguments['defaultTimeLimit'] = (int) $option[1];
698
699
                    break;
700
701
                case '--enforce-time-limit':
702
                    $this->arguments['enforceTimeLimit'] = true;
703
704
                    break;
705
706
                case '--disallow-todo-tests':
707
                    $this->arguments['disallowTodoAnnotatedTests'] = true;
708
709
                    break;
710
711
                case '--reverse-list':
712
                    $this->arguments['reverseList'] = true;
713
714
                    break;
715
716
                case '--check-version':
717
                    $this->handleVersionCheck();
718
719
                    break;
720
721
                case '--whitelist':
722
                    $this->arguments['whitelist'] = $option[1];
723
724
                    break;
725
726
                case '--random-order':
727
                    $this->handleOrderByOption('random');
728
729
                    break;
730
731
                case '--random-order-seed':
732
                    $this->arguments['randomOrderSeed'] = (int) $option[1];
733
734
                    break;
735
736
                case '--resolve-dependencies':
737
                    $this->handleOrderByOption('depends');
738
739
                    break;
740
741
                case '--ignore-dependencies':
742
                    $this->arguments['resolveDependencies'] = false;
743
744
                    break;
745
746
                case '--reverse-order':
747
                    $this->handleOrderByOption('reverse');
748
749
                    break;
750
751
                case '--dump-xdebug-filter':
752
                    $this->arguments['xdebugFilterFile'] = $option[1];
753
754
                    break;
755
756
                default:
757
                    $optionName = \str_replace('--', '', $option[0]);
758
759
                    $handler = null;
760
761
                    if (isset($this->longOptions[$optionName])) {
762
                        $handler = $this->longOptions[$optionName];
763
                    } elseif (isset($this->longOptions[$optionName . '='])) {
764
                        $handler = $this->longOptions[$optionName . '='];
765
                    }
766
767
                    if (isset($handler) && \is_callable([$this, $handler])) {
768
                        $this->$handler($option[1]);
769
                    }
770
            }
771
        }
772
773
        $this->handleCustomTestSuite();
774
775
        if (!isset($this->arguments['test'])) {
776
            if (isset($this->options[1][0])) {
777
                $this->arguments['test'] = $this->options[1][0];
778
            }
779
780
            if (isset($this->options[1][1])) {
781
                $testFile = \realpath($this->options[1][1]);
782
783
                if ($testFile === false) {
784
                    $this->exitWithErrorMessage(
785
                        \sprintf(
786
                            'Cannot open file "%s".',
787
                            $this->options[1][1]
788
                        )
789
                    );
790
                }
791
                $this->arguments['testFile'] = $testFile;
792
            } else {
793
                $this->arguments['testFile'] = '';
794
            }
795
796
            if (isset($this->arguments['test']) &&
797
                \is_file($this->arguments['test']) &&
798
                \substr($this->arguments['test'], -5, 5) != '.phpt') {
799
                $this->arguments['testFile'] = \realpath($this->arguments['test']);
800
                $this->arguments['test']     = \substr($this->arguments['test'], 0, \strrpos($this->arguments['test'], '.'));
801
            }
802
        }
803
804
        if (!isset($this->arguments['testSuffixes'])) {
805
            $this->arguments['testSuffixes'] = ['Test.php', '.phpt'];
806
        }
807
808
        if (isset($includePath)) {
809
            \ini_set(
810
                'include_path',
811
                $includePath . \PATH_SEPARATOR . \ini_get('include_path')
812
            );
813
        }
814
815
        if ($this->arguments['loader'] !== null) {
816
            $this->arguments['loader'] = $this->handleLoader($this->arguments['loader']);
817
        }
818
819
        if (isset($this->arguments['configuration']) &&
820
            \is_dir($this->arguments['configuration'])) {
821
            $configurationFile = $this->arguments['configuration'] . '/phpunit.xml';
822
823
            if (\file_exists($configurationFile)) {
824
                $this->arguments['configuration'] = \realpath(
825
                    $configurationFile
826
                );
827
            } elseif (\file_exists($configurationFile . '.dist')) {
828
                $this->arguments['configuration'] = \realpath(
829
                    $configurationFile . '.dist'
830
                );
831
            }
832
        } elseif (!isset($this->arguments['configuration']) &&
833
            $this->arguments['useDefaultConfiguration']) {
834
            if (\file_exists('phpunit.xml')) {
835
                $this->arguments['configuration'] = \realpath('phpunit.xml');
836
            } elseif (\file_exists('phpunit.xml.dist')) {
837
                $this->arguments['configuration'] = \realpath(
838
                    'phpunit.xml.dist'
839
                );
840
            }
841
        }
842
843
        if (isset($this->arguments['configuration'])) {
844
            try {
845
                $configuration = Configuration::getInstance(
846
                    $this->arguments['configuration']
847
                );
848
            } catch (Throwable $t) {
849
                print $t->getMessage() . \PHP_EOL;
850
                exit(TestRunner::FAILURE_EXIT);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
851
            }
852
853
            $phpunitConfiguration = $configuration->getPHPUnitConfiguration();
854
855
            $configuration->handlePHPConfiguration();
856
857
            /*
858
             * Issue #1216
859
             */
860
            if (isset($this->arguments['bootstrap'])) {
861
                $this->handleBootstrap($this->arguments['bootstrap']);
862
            } elseif (isset($phpunitConfiguration['bootstrap'])) {
863
                $this->handleBootstrap($phpunitConfiguration['bootstrap']);
864
            }
865
866
            /*
867
             * Issue #657
868
             */
869
            if (isset($phpunitConfiguration['stderr']) && !isset($this->arguments['stderr'])) {
870
                $this->arguments['stderr'] = $phpunitConfiguration['stderr'];
871
            }
872
873
            if (isset($phpunitConfiguration['extensionsDirectory']) && !isset($this->arguments['noExtensions']) && \extension_loaded('phar')) {
874
                $this->handleExtensions($phpunitConfiguration['extensionsDirectory']);
875
            }
876
877
            if (isset($phpunitConfiguration['columns']) && !isset($this->arguments['columns'])) {
878
                $this->arguments['columns'] = $phpunitConfiguration['columns'];
879
            }
880
881
            if (!isset($this->arguments['printer']) && isset($phpunitConfiguration['printerClass'])) {
882
                if (isset($phpunitConfiguration['printerFile'])) {
883
                    $file = $phpunitConfiguration['printerFile'];
884
                } else {
885
                    $file = '';
886
                }
887
888
                $this->arguments['printer'] = $this->handlePrinter(
889
                    $phpunitConfiguration['printerClass'],
890
                    $file
891
                );
892
            }
893
894
            if (isset($phpunitConfiguration['testSuiteLoaderClass'])) {
895
                if (isset($phpunitConfiguration['testSuiteLoaderFile'])) {
896
                    $file = $phpunitConfiguration['testSuiteLoaderFile'];
897
                } else {
898
                    $file = '';
899
                }
900
901
                $this->arguments['loader'] = $this->handleLoader(
902
                    $phpunitConfiguration['testSuiteLoaderClass'],
903
                    $file
904
                );
905
            }
906
907
            if (!isset($this->arguments['testsuite']) && isset($phpunitConfiguration['defaultTestSuite'])) {
908
                $this->arguments['testsuite'] = $phpunitConfiguration['defaultTestSuite'];
909
            }
910
911
            if (!isset($this->arguments['test'])) {
912
                $testSuite = $configuration->getTestSuiteConfiguration($this->arguments['testsuite'] ?? '');
913
914
                if ($testSuite !== null) {
915
                    $this->arguments['test'] = $testSuite;
916
                }
917
            }
918
        } elseif (isset($this->arguments['bootstrap'])) {
919
            $this->handleBootstrap($this->arguments['bootstrap']);
920
        }
921
922
        if (isset($this->arguments['printer']) &&
923
            \is_string($this->arguments['printer'])) {
924
            $this->arguments['printer'] = $this->handlePrinter($this->arguments['printer']);
925
        }
926
927
        if (isset($this->arguments['test']) && \is_string($this->arguments['test']) && \substr($this->arguments['test'], -5, 5) == '.phpt') {
928
            $test = new PhptTestCase($this->arguments['test']);
929
930
            $this->arguments['test'] = new TestSuite;
931
            $this->arguments['test']->addTest($test);
932
        }
933
934
        if (!isset($this->arguments['test'])) {
935
            $this->showHelp();
936
            exit(TestRunner::EXCEPTION_EXIT);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
937
        }
938
    }
939
940
    /**
941
     * Handles the loading of the PHPUnit\Runner\TestSuiteLoader implementation.
942
     */
943
    protected function handleLoader(string $loaderClass, string $loaderFile = ''): ?TestSuiteLoader
944
    {
945
        if (!\class_exists($loaderClass, false)) {
946
            if ($loaderFile == '') {
947
                $loaderFile = Filesystem::classNameToFilename(
948
                    $loaderClass
949
                );
950
            }
951
952
            $loaderFile = \stream_resolve_include_path($loaderFile);
953
954
            if ($loaderFile) {
955
                require $loaderFile;
956
            }
957
        }
958
959
        if (\class_exists($loaderClass, false)) {
960
            $class = new ReflectionClass($loaderClass);
961
962
            if ($class->implementsInterface(TestSuiteLoader::class) &&
963
                $class->isInstantiable()) {
964
                return $class->newInstance();
965
            }
966
        }
967
968
        if ($loaderClass == StandardTestSuiteLoader::class) {
969
            return null;
970
        }
971
972
        $this->exitWithErrorMessage(
973
            \sprintf(
974
                'Could not use "%s" as loader.',
975
                $loaderClass
976
            )
977
        );
978
979
        return null;
980
    }
981
982
    /**
983
     * Handles the loading of the PHPUnit\Util\Printer implementation.
984
     *
985
     * @return null|Printer|string
986
     */
987
    protected function handlePrinter(string $printerClass, string $printerFile = '')
988
    {
989
        if (!\class_exists($printerClass, false)) {
990
            if ($printerFile == '') {
991
                $printerFile = Filesystem::classNameToFilename(
992
                    $printerClass
993
                );
994
            }
995
996
            $printerFile = \stream_resolve_include_path($printerFile);
997
998
            if ($printerFile) {
999
                require $printerFile;
1000
            }
1001
        }
1002
1003
        if (!\class_exists($printerClass)) {
1004
            $this->exitWithErrorMessage(
1005
                \sprintf(
1006
                    'Could not use "%s" as printer: class does not exist',
1007
                    $printerClass
1008
                )
1009
            );
1010
        }
1011
1012
        $class = new ReflectionClass($printerClass);
1013
1014
        if (!$class->implementsInterface(TestListener::class)) {
1015
            $this->exitWithErrorMessage(
1016
                \sprintf(
1017
                    'Could not use "%s" as printer: class does not implement %s',
1018
                    $printerClass,
1019
                    TestListener::class
1020
                )
1021
            );
1022
        }
1023
1024
        if (!$class->isSubclassOf(Printer::class)) {
1025
            $this->exitWithErrorMessage(
1026
                \sprintf(
1027
                    'Could not use "%s" as printer: class does not extend %s',
1028
                    $printerClass,
1029
                    Printer::class
1030
                )
1031
            );
1032
        }
1033
1034
        if (!$class->isInstantiable()) {
1035
            $this->exitWithErrorMessage(
1036
                \sprintf(
1037
                    'Could not use "%s" as printer: class cannot be instantiated',
1038
                    $printerClass
1039
                )
1040
            );
1041
        }
1042
1043
        if ($class->isSubclassOf(ResultPrinter::class)) {
1044
            return $printerClass;
1045
        }
1046
1047
        $outputStream = isset($this->arguments['stderr']) ? 'php://stderr' : null;
1048
1049
        return $class->newInstance($outputStream);
1050
    }
1051
1052
    /**
1053
     * Loads a bootstrap file.
1054
     */
1055
    protected function handleBootstrap(string $filename): void
1056
    {
1057
        try {
1058
            FileLoader::checkAndLoad($filename);
1059
        } catch (Exception $e) {
1060
            $this->exitWithErrorMessage($e->getMessage());
1061
        }
1062
    }
1063
1064
    protected function handleVersionCheck(): void
1065
    {
1066
        $this->printVersionString();
1067
1068
        $latestVersion = \file_get_contents('https://phar.phpunit.de/latest-version-of/phpunit');
1069
        $isOutdated    = \version_compare($latestVersion, Version::id(), '>');
1070
1071
        if ($isOutdated) {
1072
            \printf(
1073
                'You are not using the latest version of PHPUnit.' . \PHP_EOL .
1074
                'The latest version is PHPUnit %s.' . \PHP_EOL,
1075
                $latestVersion
1076
            );
1077
        } else {
1078
            print 'You are using the latest version of PHPUnit.' . \PHP_EOL;
1079
        }
1080
1081
        exit(TestRunner::SUCCESS_EXIT);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1082
    }
1083
1084
    /**
1085
     * Show the help message.
1086
     */
1087
    protected function showHelp(): void
1088
    {
1089
        $this->printVersionString();
1090
1091
        print <<<EOT
1092
Usage: phpunit [options] UnitTest [UnitTest.php]
1093
       phpunit [options] <directory>
1094
1095
Code Coverage Options:
1096
1097
  --coverage-clover <file>    Generate code coverage report in Clover XML format
1098
  --coverage-crap4j <file>    Generate code coverage report in Crap4J XML format
1099
  --coverage-html <dir>       Generate code coverage report in HTML format
1100
  --coverage-php <file>       Export PHP_CodeCoverage object to file
1101
  --coverage-text=<file>      Generate code coverage report in text format
1102
                              Default: Standard output
1103
  --coverage-xml <dir>        Generate code coverage report in PHPUnit XML format
1104
  --whitelist <dir>           Whitelist <dir> for code coverage analysis
1105
  --disable-coverage-ignore   Disable annotations for ignoring code coverage
1106
  --no-coverage               Ignore code coverage configuration
1107
  --dump-xdebug-filter <file> Generate script to set Xdebug code coverage filter
1108
1109
Logging Options:
1110
1111
  --log-junit <file>          Log test execution in JUnit XML format to file
1112
  --log-teamcity <file>       Log test execution in TeamCity format to file
1113
  --testdox-html <file>       Write agile documentation in HTML format to file
1114
  --testdox-text <file>       Write agile documentation in Text format to file
1115
  --testdox-xml <file>        Write agile documentation in XML format to file
1116
  --reverse-list              Print defects in reverse order
1117
1118
Test Selection Options:
1119
1120
  --filter <pattern>          Filter which tests to run
1121
  --testsuite <name,...>      Filter which testsuite to run
1122
  --group ...                 Only runs tests from the specified group(s)
1123
  --exclude-group ...         Exclude tests from the specified group(s)
1124
  --list-groups               List available test groups
1125
  --list-suites               List available test suites
1126
  --list-tests                List available tests
1127
  --list-tests-xml <file>     List available tests in XML format
1128
  --test-suffix ...           Only search for test in files with specified
1129
                              suffix(es). Default: Test.php,.phpt
1130
1131
Test Execution Options:
1132
1133
  --dont-report-useless-tests Do not report tests that do not test anything
1134
  --strict-coverage           Be strict about @covers annotation usage
1135
  --strict-global-state       Be strict about changes to global state
1136
  --disallow-test-output      Be strict about output during tests
1137
  --disallow-resource-usage   Be strict about resource usage during small tests
1138
  --enforce-time-limit        Enforce time limit based on test size
1139
  --default-time-limit=<sec>  Timeout in seconds for tests without @small, @medium or @large
1140
  --disallow-todo-tests       Disallow @todo-annotated tests
1141
1142
  --process-isolation         Run each test in a separate PHP process
1143
  --globals-backup            Backup and restore \$GLOBALS for each test
1144
  --static-backup             Backup and restore static attributes for each test
1145
1146
  --colors=<flag>             Use colors in output ("never", "auto" or "always")
1147
  --columns <n>               Number of columns to use for progress output
1148
  --columns max               Use maximum number of columns for progress output
1149
  --stderr                    Write to STDERR instead of STDOUT
1150
  --stop-on-defect            Stop execution upon first not-passed test
1151
  --stop-on-error             Stop execution upon first error
1152
  --stop-on-failure           Stop execution upon first error or failure
1153
  --stop-on-warning           Stop execution upon first warning
1154
  --stop-on-risky             Stop execution upon first risky test
1155
  --stop-on-skipped           Stop execution upon first skipped test
1156
  --stop-on-incomplete        Stop execution upon first incomplete test
1157
  --fail-on-warning           Treat tests with warnings as failures
1158
  --fail-on-risky             Treat risky tests as failures
1159
  -v|--verbose                Output more verbose information
1160
  --debug                     Display debugging information
1161
1162
  --loader <loader>           TestSuiteLoader implementation to use
1163
  --repeat <times>            Runs the test(s) repeatedly
1164
  --teamcity                  Report test execution progress in TeamCity format
1165
  --testdox                   Report test execution progress in TestDox format
1166
  --testdox-group             Only include tests from the specified group(s)
1167
  --testdox-exclude-group     Exclude tests from the specified group(s)
1168
  --printer <printer>         TestListener implementation to use
1169
1170
  --resolve-dependencies      Resolve dependencies between tests
1171
  --order-by=<order>          Run tests in order: default|reverse|random|defects|depends
1172
  --random-order-seed=<N>     Use a specific random seed <N> for random order
1173
  --cache-result              Write run result to cache to enable ordering tests defects-first
1174
1175
Configuration Options:
1176
1177
  --prepend <file>            A PHP script that is included as early as possible
1178
  --bootstrap <file>          A PHP script that is included before the tests run
1179
  -c|--configuration <file>   Read configuration from XML file
1180
  --no-configuration          Ignore default configuration file (phpunit.xml)
1181
  --no-logging                Ignore logging configuration
1182
  --no-extensions             Do not load PHPUnit extensions
1183
  --include-path <path(s)>    Prepend PHP's include_path with given path(s)
1184
  -d key[=value]              Sets a php.ini value
1185
  --generate-configuration    Generate configuration file with suggested settings
1186
  --cache-result-file=<file>  Specify result cache path and filename
1187
1188
Miscellaneous Options:
1189
1190
  -h|--help                   Prints this usage information
1191
  --version                   Prints the version and exits
1192
  --atleast-version <min>     Checks that version is greater than min and exits
1193
  --check-version             Check whether PHPUnit is the latest version
1194
1195
EOT;
1196
    }
1197
1198
    /**
1199
     * Custom callback for test suite discovery.
1200
     */
1201
    protected function handleCustomTestSuite(): void
1202
    {
1203
    }
1204
1205
    private function printVersionString(): void
1206
    {
1207
        if ($this->versionStringPrinted) {
1208
            return;
1209
        }
1210
1211
        print Version::getVersionString() . \PHP_EOL . \PHP_EOL;
1212
1213
        $this->versionStringPrinted = true;
1214
    }
1215
1216
    private function exitWithErrorMessage(string $message): void
1217
    {
1218
        $this->printVersionString();
1219
1220
        print $message . \PHP_EOL;
1221
1222
        exit(TestRunner::FAILURE_EXIT);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1223
    }
1224
1225
    private function handleExtensions(string $directory): void
1226
    {
1227
        $facade = new FileIteratorFacade;
1228
1229
        foreach ($facade->getFilesAsArray($directory, '.phar') as $file) {
1230
            if (!\file_exists('phar://' . $file . '/manifest.xml')) {
1231
                $this->arguments['notLoadedExtensions'][] = $file . ' is not an extension for PHPUnit';
1232
1233
                continue;
1234
            }
1235
1236
            try {
1237
                $applicationName = new ApplicationName('phpunit/phpunit');
1238
                $version         = new PharIoVersion(Version::series());
1239
                $manifest        = ManifestLoader::fromFile('phar://' . $file . '/manifest.xml');
1240
1241
                if (!$manifest->isExtensionFor($applicationName)) {
1242
                    $this->arguments['notLoadedExtensions'][] = $file . ' is not an extension for PHPUnit';
1243
1244
                    continue;
1245
                }
1246
1247
                if (!$manifest->isExtensionFor($applicationName, $version)) {
1248
                    $this->arguments['notLoadedExtensions'][] = $file . ' is not compatible with this version of PHPUnit';
1249
1250
                    continue;
1251
                }
1252
            } catch (ManifestException $e) {
1253
                $this->arguments['notLoadedExtensions'][] = $file . ': ' . $e->getMessage();
1254
1255
                continue;
1256
            }
1257
1258
            require $file;
1259
1260
            $this->arguments['loadedExtensions'][] = $manifest->getName() . ' ' . $manifest->getVersion()->getVersionString();
1261
        }
1262
    }
1263
1264
    private function handleListGroups(TestSuite $suite, bool $exit): int
1265
    {
1266
        $this->printVersionString();
1267
1268
        print 'Available test group(s):' . \PHP_EOL;
1269
1270
        $groups = $suite->getGroups();
1271
        \sort($groups);
1272
1273
        foreach ($groups as $group) {
1274
            \printf(
1275
                ' - %s' . \PHP_EOL,
1276
                $group
1277
            );
1278
        }
1279
1280
        if ($exit) {
1281
            exit(TestRunner::SUCCESS_EXIT);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return integer. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
1282
        }
1283
1284
        return TestRunner::SUCCESS_EXIT;
1285
    }
1286
1287
    private function handleListSuites(bool $exit): int
1288
    {
1289
        $this->printVersionString();
1290
1291
        print 'Available test suite(s):' . \PHP_EOL;
1292
1293
        $configuration = Configuration::getInstance(
1294
            $this->arguments['configuration']
1295
        );
1296
1297
        $suiteNames = $configuration->getTestSuiteNames();
1298
1299
        foreach ($suiteNames as $suiteName) {
1300
            \printf(
1301
                ' - %s' . \PHP_EOL,
1302
                $suiteName
1303
            );
1304
        }
1305
1306
        if ($exit) {
1307
            exit(TestRunner::SUCCESS_EXIT);
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return integer. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1308
        }
1309
1310
        return TestRunner::SUCCESS_EXIT;
1311
    }
1312
1313
    private function handleListTests(TestSuite $suite, bool $exit): int
1314
    {
1315
        $this->printVersionString();
1316
1317
        $renderer = new TextTestListRenderer;
1318
1319
        print $renderer->render($suite);
1320
1321
        if ($exit) {
1322
            exit(TestRunner::SUCCESS_EXIT);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return integer. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
1323
        }
1324
1325
        return TestRunner::SUCCESS_EXIT;
1326
    }
1327
1328
    private function handleListTestsXml(TestSuite $suite, string $target, bool $exit): int
1329
    {
1330
        $this->printVersionString();
1331
1332
        $renderer = new XmlTestListRenderer;
1333
1334
        \file_put_contents($target, $renderer->render($suite));
1335
1336
        \printf(
1337
            'Wrote list of tests that would have been run to %s' . \PHP_EOL,
1338
            $target
1339
        );
1340
1341
        if ($exit) {
1342
            exit(TestRunner::SUCCESS_EXIT);
0 ignored issues
show
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return integer. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1343
        }
1344
1345
        return TestRunner::SUCCESS_EXIT;
1346
    }
1347
1348
    private function handleOrderByOption(string $value): void
1349
    {
1350
        foreach (\explode(',', $value) as $order) {
1351
            switch ($order) {
1352
                case 'default':
1353
                    $this->arguments['executionOrder']        = TestSuiteSorter::ORDER_DEFAULT;
1354
                    $this->arguments['executionOrderDefects'] = TestSuiteSorter::ORDER_DEFAULT;
1355
                    $this->arguments['resolveDependencies']   = false;
1356
1357
                    break;
1358
1359
                case 'reverse':
1360
                    $this->arguments['executionOrder'] = TestSuiteSorter::ORDER_REVERSED;
1361
1362
                    break;
1363
1364
                case 'random':
1365
                    $this->arguments['executionOrder'] = TestSuiteSorter::ORDER_RANDOMIZED;
1366
1367
                    break;
1368
1369
                case 'defects':
1370
                    $this->arguments['executionOrderDefects'] = TestSuiteSorter::ORDER_DEFECTS_FIRST;
1371
1372
                    break;
1373
1374
                case 'depends':
1375
                    $this->arguments['resolveDependencies'] = true;
1376
1377
                    break;
1378
1379
                default:
1380
                    $this->exitWithErrorMessage("unrecognized --order-by option: $order");
1381
            }
1382
        }
1383
    }
1384
}
1385