Total Complexity | 206 |
Total Lines | 1337 |
Duplicated Lines | 0 % |
Changes | 0 |
Complex classes like Command often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Command, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
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); |
||
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); |
||
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); |
||
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); |
||
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); |
||
654 | } |
||
655 | |||
656 | exit(TestRunner::FAILURE_EXIT); |
||
657 | |||
658 | break; |
||
659 | |||
660 | case '--version': |
||
661 | $this->printVersionString(); |
||
662 | exit(TestRunner::SUCCESS_EXIT); |
||
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); |
||
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); |
||
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); |
||
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 |
||
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); |
||
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); |
||
1282 | } |
||
1283 | |||
1284 | return TestRunner::SUCCESS_EXIT; |
||
1285 | } |
||
1286 | |||
1287 | private function handleListSuites(bool $exit): int |
||
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); |
||
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); |
||
1343 | } |
||
1344 | |||
1345 | return TestRunner::SUCCESS_EXIT; |
||
1346 | } |
||
1347 | |||
1348 | private function handleOrderByOption(string $value): void |
||
1381 | } |
||
1382 | } |
||
1383 | } |
||
1384 | } |
||
1385 |
In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.