1<?php 2/* 3 * This file is part of PHPUnit. 4 * 5 * (c) Sebastian Bergmann <sebastian@phpunit.de> 6 * 7 * For the full copyright and license information, please view the LICENSE 8 * file that was distributed with this source code. 9 */ 10 11/** 12 * Wrapper for the PHPUnit XML configuration file. 13 * 14 * Example XML configuration file: 15 * <code> 16 * <?xml version="1.0" encoding="utf-8" ?> 17 * 18 * <phpunit backupGlobals="true" 19 * backupStaticAttributes="false" 20 * bootstrap="/path/to/bootstrap.php" 21 * cacheTokens="false" 22 * columns="80" 23 * colors="false" 24 * stderr="false" 25 * convertErrorsToExceptions="true" 26 * convertNoticesToExceptions="true" 27 * convertWarningsToExceptions="true" 28 * forceCoversAnnotation="false" 29 * processIsolation="false" 30 * stopOnError="false" 31 * stopOnFailure="false" 32 * stopOnWarning="false" 33 * stopOnIncomplete="false" 34 * stopOnRisky="false" 35 * stopOnSkipped="false" 36 * failOnWarning="false" 37 * failOnRisky="false" 38 * extensionsDirectory="tools/phpunit.d" 39 * printerClass="PHPUnit_TextUI_ResultPrinter" 40 * testSuiteLoaderClass="PHPUnit_Runner_StandardTestSuiteLoader" 41 * beStrictAboutChangesToGlobalState="false" 42 * beStrictAboutCoversAnnotation="false" 43 * beStrictAboutOutputDuringTests="false" 44 * beStrictAboutResourceUsageDuringSmallTests="false" 45 * beStrictAboutTestsThatDoNotTestAnything="false" 46 * beStrictAboutTodoAnnotatedTests="false" 47 * checkForUnintentionallyCoveredCode="false" 48 * enforceTimeLimit="false" 49 * timeoutForSmallTests="1" 50 * timeoutForMediumTests="10" 51 * timeoutForLargeTests="60" 52 * verbose="false" 53 * reverseDefectList="false" 54 * registerMockObjectsFromTestArgumentsRecursively="false"> 55 * <testsuites> 56 * <testsuite name="My Test Suite"> 57 * <directory suffix="Test.php" phpVersion="5.3.0" phpVersionOperator=">=">/path/to/files</directory> 58 * <file phpVersion="5.3.0" phpVersionOperator=">=">/path/to/MyTest.php</file> 59 * <exclude>/path/to/files/exclude</exclude> 60 * </testsuite> 61 * </testsuites> 62 * 63 * <groups> 64 * <include> 65 * <group>name</group> 66 * </include> 67 * <exclude> 68 * <group>name</group> 69 * </exclude> 70 * </groups> 71 * 72 * <testdoxGroups> 73 * <include> 74 * <group>name</group> 75 * </include> 76 * <exclude> 77 * <group>name</group> 78 * </exclude> 79 * </testdoxGroups> 80 * 81 * <filter> 82 * <whitelist addUncoveredFilesFromWhitelist="true" 83 * processUncoveredFilesFromWhitelist="false"> 84 * <directory suffix=".php">/path/to/files</directory> 85 * <file>/path/to/file</file> 86 * <exclude> 87 * <directory suffix=".php">/path/to/files</directory> 88 * <file>/path/to/file</file> 89 * </exclude> 90 * </whitelist> 91 * </filter> 92 * 93 * <listeners> 94 * <listener class="MyListener" file="/optional/path/to/MyListener.php"> 95 * <arguments> 96 * <array> 97 * <element key="0"> 98 * <string>Sebastian</string> 99 * </element> 100 * </array> 101 * <integer>22</integer> 102 * <string>April</string> 103 * <double>19.78</double> 104 * <null/> 105 * <object class="stdClass"/> 106 * <file>MyRelativeFile.php</file> 107 * <directory>MyRelativeDir</directory> 108 * </arguments> 109 * </listener> 110 * </listeners> 111 * 112 * <logging> 113 * <log type="coverage-html" target="/tmp/report" lowUpperBound="50" highLowerBound="90"/> 114 * <log type="coverage-clover" target="/tmp/clover.xml"/> 115 * <log type="coverage-crap4j" target="/tmp/crap.xml" threshold="30"/> 116 * <log type="json" target="/tmp/logfile.json"/> 117 * <log type="plain" target="/tmp/logfile.txt"/> 118 * <log type="tap" target="/tmp/logfile.tap"/> 119 * <log type="teamcity" target="/tmp/logfile.txt"/> 120 * <log type="junit" target="/tmp/logfile.xml" logIncompleteSkipped="false"/> 121 * <log type="testdox-html" target="/tmp/testdox.html"/> 122 * <log type="testdox-text" target="/tmp/testdox.txt"/> 123 * <log type="testdox-xml" target="/tmp/testdox.xml"/> 124 * </logging> 125 * 126 * <php> 127 * <includePath>.</includePath> 128 * <ini name="foo" value="bar"/> 129 * <const name="foo" value="bar"/> 130 * <var name="foo" value="bar"/> 131 * <env name="foo" value="bar"/> 132 * <post name="foo" value="bar"/> 133 * <get name="foo" value="bar"/> 134 * <cookie name="foo" value="bar"/> 135 * <server name="foo" value="bar"/> 136 * <files name="foo" value="bar"/> 137 * <request name="foo" value="bar"/> 138 * </php> 139 * </phpunit> 140 * </code> 141 */ 142class PHPUnit_Util_Configuration 143{ 144 private static $instances = []; 145 146 protected $document; 147 protected $xpath; 148 protected $filename; 149 150 /** 151 * Loads a PHPUnit configuration file. 152 * 153 * @param string $filename 154 */ 155 protected function __construct($filename) 156 { 157 $this->filename = $filename; 158 $this->document = PHPUnit_Util_XML::loadFile($filename, false, true, true); 159 $this->xpath = new DOMXPath($this->document); 160 } 161 162 final private function __clone() 163 { 164 } 165 166 /** 167 * Returns a PHPUnit configuration object. 168 * 169 * @param string $filename 170 * 171 * @return PHPUnit_Util_Configuration 172 */ 173 public static function getInstance($filename) 174 { 175 $realpath = realpath($filename); 176 177 if ($realpath === false) { 178 throw new PHPUnit_Framework_Exception( 179 sprintf( 180 'Could not read "%s".', 181 $filename 182 ) 183 ); 184 } 185 186 if (!isset(self::$instances[$realpath])) { 187 self::$instances[$realpath] = new self($realpath); 188 } 189 190 return self::$instances[$realpath]; 191 } 192 193 /** 194 * Returns the realpath to the configuration file. 195 * 196 * @return string 197 */ 198 public function getFilename() 199 { 200 return $this->filename; 201 } 202 203 /** 204 * Returns the configuration for SUT filtering. 205 * 206 * @return array 207 */ 208 public function getFilterConfiguration() 209 { 210 $addUncoveredFilesFromWhitelist = true; 211 $processUncoveredFilesFromWhitelist = false; 212 213 $tmp = $this->xpath->query('filter/whitelist'); 214 215 if ($tmp->length == 1) { 216 if ($tmp->item(0)->hasAttribute('addUncoveredFilesFromWhitelist')) { 217 $addUncoveredFilesFromWhitelist = $this->getBoolean( 218 (string) $tmp->item(0)->getAttribute( 219 'addUncoveredFilesFromWhitelist' 220 ), 221 true 222 ); 223 } 224 225 if ($tmp->item(0)->hasAttribute('processUncoveredFilesFromWhitelist')) { 226 $processUncoveredFilesFromWhitelist = $this->getBoolean( 227 (string) $tmp->item(0)->getAttribute( 228 'processUncoveredFilesFromWhitelist' 229 ), 230 false 231 ); 232 } 233 } 234 235 return [ 236 'whitelist' => [ 237 'addUncoveredFilesFromWhitelist' => $addUncoveredFilesFromWhitelist, 238 'processUncoveredFilesFromWhitelist' => $processUncoveredFilesFromWhitelist, 239 'include' => [ 240 'directory' => $this->readFilterDirectories( 241 'filter/whitelist/directory' 242 ), 243 'file' => $this->readFilterFiles( 244 'filter/whitelist/file' 245 ) 246 ], 247 'exclude' => [ 248 'directory' => $this->readFilterDirectories( 249 'filter/whitelist/exclude/directory' 250 ), 251 'file' => $this->readFilterFiles( 252 'filter/whitelist/exclude/file' 253 ) 254 ] 255 ] 256 ]; 257 } 258 259 /** 260 * Returns the configuration for groups. 261 * 262 * @return array 263 */ 264 public function getGroupConfiguration() 265 { 266 return $this->parseGroupConfiguration('groups'); 267 } 268 269 /** 270 * Returns the configuration for testdox groups. 271 * 272 * @return array 273 */ 274 public function getTestdoxGroupConfiguration() 275 { 276 return $this->parseGroupConfiguration('testdoxGroups'); 277 } 278 279 /** 280 * @param string $root 281 * 282 * @return array 283 */ 284 private function parseGroupConfiguration($root) 285 { 286 $groups = [ 287 'include' => [], 288 'exclude' => [] 289 ]; 290 291 foreach ($this->xpath->query($root . '/include/group') as $group) { 292 $groups['include'][] = (string) $group->textContent; 293 } 294 295 foreach ($this->xpath->query($root . '/exclude/group') as $group) { 296 $groups['exclude'][] = (string) $group->textContent; 297 } 298 299 return $groups; 300 } 301 302 /** 303 * Returns the configuration for listeners. 304 * 305 * @return array 306 */ 307 public function getListenerConfiguration() 308 { 309 $result = []; 310 311 foreach ($this->xpath->query('listeners/listener') as $listener) { 312 $class = (string) $listener->getAttribute('class'); 313 $file = ''; 314 $arguments = []; 315 316 if ($listener->getAttribute('file')) { 317 $file = $this->toAbsolutePath( 318 (string) $listener->getAttribute('file'), 319 true 320 ); 321 } 322 323 foreach ($listener->childNodes as $node) { 324 if ($node instanceof DOMElement && $node->tagName == 'arguments') { 325 foreach ($node->childNodes as $argument) { 326 if ($argument instanceof DOMElement) { 327 if ($argument->tagName == 'file' || 328 $argument->tagName == 'directory') { 329 $arguments[] = $this->toAbsolutePath((string) $argument->textContent); 330 } else { 331 $arguments[] = PHPUnit_Util_XML::xmlToVariable($argument); 332 } 333 } 334 } 335 } 336 } 337 338 $result[] = [ 339 'class' => $class, 340 'file' => $file, 341 'arguments' => $arguments 342 ]; 343 } 344 345 return $result; 346 } 347 348 /** 349 * Returns the logging configuration. 350 * 351 * @return array 352 */ 353 public function getLoggingConfiguration() 354 { 355 $result = []; 356 357 foreach ($this->xpath->query('logging/log') as $log) { 358 $type = (string) $log->getAttribute('type'); 359 $target = (string) $log->getAttribute('target'); 360 361 if (!$target) { 362 continue; 363 } 364 365 $target = $this->toAbsolutePath($target); 366 367 if ($type == 'coverage-html') { 368 if ($log->hasAttribute('lowUpperBound')) { 369 $result['lowUpperBound'] = $this->getInteger( 370 (string) $log->getAttribute('lowUpperBound'), 371 50 372 ); 373 } 374 375 if ($log->hasAttribute('highLowerBound')) { 376 $result['highLowerBound'] = $this->getInteger( 377 (string) $log->getAttribute('highLowerBound'), 378 90 379 ); 380 } 381 } elseif ($type == 'coverage-crap4j') { 382 if ($log->hasAttribute('threshold')) { 383 $result['crap4jThreshold'] = $this->getInteger( 384 (string) $log->getAttribute('threshold'), 385 30 386 ); 387 } 388 } elseif ($type == 'junit') { 389 if ($log->hasAttribute('logIncompleteSkipped')) { 390 $result['logIncompleteSkipped'] = $this->getBoolean( 391 (string) $log->getAttribute('logIncompleteSkipped'), 392 false 393 ); 394 } 395 } elseif ($type == 'coverage-text') { 396 if ($log->hasAttribute('showUncoveredFiles')) { 397 $result['coverageTextShowUncoveredFiles'] = $this->getBoolean( 398 (string) $log->getAttribute('showUncoveredFiles'), 399 false 400 ); 401 } 402 if ($log->hasAttribute('showOnlySummary')) { 403 $result['coverageTextShowOnlySummary'] = $this->getBoolean( 404 (string) $log->getAttribute('showOnlySummary'), 405 false 406 ); 407 } 408 } 409 410 $result[$type] = $target; 411 } 412 413 return $result; 414 } 415 416 /** 417 * Returns the PHP configuration. 418 * 419 * @return array 420 */ 421 public function getPHPConfiguration() 422 { 423 $result = [ 424 'include_path' => [], 425 'ini' => [], 426 'const' => [], 427 'var' => [], 428 'env' => [], 429 'post' => [], 430 'get' => [], 431 'cookie' => [], 432 'server' => [], 433 'files' => [], 434 'request' => [] 435 ]; 436 437 foreach ($this->xpath->query('php/includePath') as $includePath) { 438 $path = (string) $includePath->textContent; 439 if ($path) { 440 $result['include_path'][] = $this->toAbsolutePath($path); 441 } 442 } 443 444 foreach ($this->xpath->query('php/ini') as $ini) { 445 $name = (string) $ini->getAttribute('name'); 446 $value = (string) $ini->getAttribute('value'); 447 448 $result['ini'][$name] = $value; 449 } 450 451 foreach ($this->xpath->query('php/const') as $const) { 452 $name = (string) $const->getAttribute('name'); 453 $value = (string) $const->getAttribute('value'); 454 455 $result['const'][$name] = $this->getBoolean($value, $value); 456 } 457 458 foreach (['var', 'env', 'post', 'get', 'cookie', 'server', 'files', 'request'] as $array) { 459 foreach ($this->xpath->query('php/' . $array) as $var) { 460 $name = (string) $var->getAttribute('name'); 461 $value = (string) $var->getAttribute('value'); 462 463 $result[$array][$name] = $this->getBoolean($value, $value); 464 } 465 } 466 467 return $result; 468 } 469 470 /** 471 * Handles the PHP configuration. 472 */ 473 public function handlePHPConfiguration() 474 { 475 $configuration = $this->getPHPConfiguration(); 476 477 if (! empty($configuration['include_path'])) { 478 ini_set( 479 'include_path', 480 implode(PATH_SEPARATOR, $configuration['include_path']) . 481 PATH_SEPARATOR . 482 ini_get('include_path') 483 ); 484 } 485 486 foreach ($configuration['ini'] as $name => $value) { 487 if (defined($value)) { 488 $value = constant($value); 489 } 490 491 ini_set($name, $value); 492 } 493 494 foreach ($configuration['const'] as $name => $value) { 495 if (!defined($name)) { 496 define($name, $value); 497 } 498 } 499 500 foreach (['var', 'post', 'get', 'cookie', 'server', 'files', 'request'] as $array) { 501 // See https://github.com/sebastianbergmann/phpunit/issues/277 502 switch ($array) { 503 case 'var': 504 $target = &$GLOBALS; 505 break; 506 507 case 'server': 508 $target = &$_SERVER; 509 break; 510 511 default: 512 $target = &$GLOBALS['_' . strtoupper($array)]; 513 break; 514 } 515 516 foreach ($configuration[$array] as $name => $value) { 517 $target[$name] = $value; 518 } 519 } 520 521 foreach ($configuration['env'] as $name => $value) { 522 if (false === getenv($name)) { 523 putenv("{$name}={$value}"); 524 } 525 if (!isset($_ENV[$name])) { 526 $_ENV[$name] = $value; 527 } 528 } 529 } 530 531 /** 532 * Returns the PHPUnit configuration. 533 * 534 * @return array 535 */ 536 public function getPHPUnitConfiguration() 537 { 538 $result = []; 539 $root = $this->document->documentElement; 540 541 if ($root->hasAttribute('cacheTokens')) { 542 $result['cacheTokens'] = $this->getBoolean( 543 (string) $root->getAttribute('cacheTokens'), 544 false 545 ); 546 } 547 548 if ($root->hasAttribute('columns')) { 549 $columns = (string) $root->getAttribute('columns'); 550 551 if ($columns == 'max') { 552 $result['columns'] = 'max'; 553 } else { 554 $result['columns'] = $this->getInteger($columns, 80); 555 } 556 } 557 558 if ($root->hasAttribute('colors')) { 559 /* only allow boolean for compatibility with previous versions 560 'always' only allowed from command line */ 561 if ($this->getBoolean($root->getAttribute('colors'), false)) { 562 $result['colors'] = PHPUnit_TextUI_ResultPrinter::COLOR_AUTO; 563 } else { 564 $result['colors'] = PHPUnit_TextUI_ResultPrinter::COLOR_NEVER; 565 } 566 } 567 568 /* 569 * Issue #657 570 */ 571 if ($root->hasAttribute('stderr')) { 572 $result['stderr'] = $this->getBoolean( 573 (string) $root->getAttribute('stderr'), 574 false 575 ); 576 } 577 578 if ($root->hasAttribute('backupGlobals')) { 579 $result['backupGlobals'] = $this->getBoolean( 580 (string) $root->getAttribute('backupGlobals'), 581 true 582 ); 583 } 584 585 if ($root->hasAttribute('backupStaticAttributes')) { 586 $result['backupStaticAttributes'] = $this->getBoolean( 587 (string) $root->getAttribute('backupStaticAttributes'), 588 false 589 ); 590 } 591 592 if ($root->getAttribute('bootstrap')) { 593 $result['bootstrap'] = $this->toAbsolutePath( 594 (string) $root->getAttribute('bootstrap') 595 ); 596 } 597 598 if ($root->hasAttribute('convertErrorsToExceptions')) { 599 $result['convertErrorsToExceptions'] = $this->getBoolean( 600 (string) $root->getAttribute('convertErrorsToExceptions'), 601 true 602 ); 603 } 604 605 if ($root->hasAttribute('convertNoticesToExceptions')) { 606 $result['convertNoticesToExceptions'] = $this->getBoolean( 607 (string) $root->getAttribute('convertNoticesToExceptions'), 608 true 609 ); 610 } 611 612 if ($root->hasAttribute('convertWarningsToExceptions')) { 613 $result['convertWarningsToExceptions'] = $this->getBoolean( 614 (string) $root->getAttribute('convertWarningsToExceptions'), 615 true 616 ); 617 } 618 619 if ($root->hasAttribute('forceCoversAnnotation')) { 620 $result['forceCoversAnnotation'] = $this->getBoolean( 621 (string) $root->getAttribute('forceCoversAnnotation'), 622 false 623 ); 624 } 625 626 if ($root->hasAttribute('disableCodeCoverageIgnore')) { 627 $result['disableCodeCoverageIgnore'] = $this->getBoolean( 628 (string) $root->getAttribute('disableCodeCoverageIgnore'), 629 false 630 ); 631 } 632 633 if ($root->hasAttribute('processIsolation')) { 634 $result['processIsolation'] = $this->getBoolean( 635 (string) $root->getAttribute('processIsolation'), 636 false 637 ); 638 } 639 640 if ($root->hasAttribute('stopOnError')) { 641 $result['stopOnError'] = $this->getBoolean( 642 (string) $root->getAttribute('stopOnError'), 643 false 644 ); 645 } 646 647 if ($root->hasAttribute('stopOnFailure')) { 648 $result['stopOnFailure'] = $this->getBoolean( 649 (string) $root->getAttribute('stopOnFailure'), 650 false 651 ); 652 } 653 654 if ($root->hasAttribute('stopOnWarning')) { 655 $result['stopOnWarning'] = $this->getBoolean( 656 (string) $root->getAttribute('stopOnWarning'), 657 false 658 ); 659 } 660 661 if ($root->hasAttribute('stopOnIncomplete')) { 662 $result['stopOnIncomplete'] = $this->getBoolean( 663 (string) $root->getAttribute('stopOnIncomplete'), 664 false 665 ); 666 } 667 668 if ($root->hasAttribute('stopOnRisky')) { 669 $result['stopOnRisky'] = $this->getBoolean( 670 (string) $root->getAttribute('stopOnRisky'), 671 false 672 ); 673 } 674 675 if ($root->hasAttribute('stopOnSkipped')) { 676 $result['stopOnSkipped'] = $this->getBoolean( 677 (string) $root->getAttribute('stopOnSkipped'), 678 false 679 ); 680 } 681 682 if ($root->hasAttribute('failOnWarning')) { 683 $result['failOnWarning'] = $this->getBoolean( 684 (string) $root->getAttribute('failOnWarning'), 685 false 686 ); 687 } 688 689 if ($root->hasAttribute('failOnRisky')) { 690 $result['failOnRisky'] = $this->getBoolean( 691 (string) $root->getAttribute('failOnRisky'), 692 false 693 ); 694 } 695 696 if ($root->hasAttribute('testSuiteLoaderClass')) { 697 $result['testSuiteLoaderClass'] = (string) $root->getAttribute( 698 'testSuiteLoaderClass' 699 ); 700 } 701 702 if ($root->getAttribute('testSuiteLoaderFile')) { 703 $result['testSuiteLoaderFile'] = $this->toAbsolutePath( 704 (string) $root->getAttribute('testSuiteLoaderFile') 705 ); 706 } 707 708 if ($root->hasAttribute('printerClass')) { 709 $result['printerClass'] = (string) $root->getAttribute( 710 'printerClass' 711 ); 712 } 713 714 if ($root->getAttribute('printerFile')) { 715 $result['printerFile'] = $this->toAbsolutePath( 716 (string) $root->getAttribute('printerFile') 717 ); 718 } 719 720 if ($root->hasAttribute('beStrictAboutChangesToGlobalState')) { 721 $result['beStrictAboutChangesToGlobalState'] = $this->getBoolean( 722 (string) $root->getAttribute('beStrictAboutChangesToGlobalState'), 723 false 724 ); 725 } 726 727 if ($root->hasAttribute('beStrictAboutOutputDuringTests')) { 728 $result['disallowTestOutput'] = $this->getBoolean( 729 (string) $root->getAttribute('beStrictAboutOutputDuringTests'), 730 false 731 ); 732 } 733 734 if ($root->hasAttribute('beStrictAboutResourceUsageDuringSmallTests')) { 735 $result['beStrictAboutResourceUsageDuringSmallTests'] = $this->getBoolean( 736 (string) $root->getAttribute('beStrictAboutResourceUsageDuringSmallTests'), 737 false 738 ); 739 } 740 741 if ($root->hasAttribute('beStrictAboutTestsThatDoNotTestAnything')) { 742 $result['reportUselessTests'] = $this->getBoolean( 743 (string) $root->getAttribute('beStrictAboutTestsThatDoNotTestAnything'), 744 false 745 ); 746 } 747 748 if ($root->hasAttribute('beStrictAboutTodoAnnotatedTests')) { 749 $result['disallowTodoAnnotatedTests'] = $this->getBoolean( 750 (string) $root->getAttribute('beStrictAboutTodoAnnotatedTests'), 751 false 752 ); 753 } 754 755 if ($root->hasAttribute('beStrictAboutCoversAnnotation')) { 756 $result['strictCoverage'] = $this->getBoolean( 757 (string) $root->getAttribute('beStrictAboutCoversAnnotation'), 758 false 759 ); 760 } elseif ($root->hasAttribute('checkForUnintentionallyCoveredCode')) { 761 $result['strictCoverage'] = $this->getBoolean( 762 (string) $root->getAttribute('checkForUnintentionallyCoveredCode'), 763 false 764 ); 765 766 $result['deprecatedCheckForUnintentionallyCoveredCodeSettingUsed'] = true; 767 } 768 769 if ($root->hasAttribute('enforceTimeLimit')) { 770 $result['enforceTimeLimit'] = $this->getBoolean( 771 (string) $root->getAttribute('enforceTimeLimit'), 772 false 773 ); 774 } 775 776 if ($root->hasAttribute('timeoutForSmallTests')) { 777 $result['timeoutForSmallTests'] = $this->getInteger( 778 (string) $root->getAttribute('timeoutForSmallTests'), 779 1 780 ); 781 } 782 783 if ($root->hasAttribute('timeoutForMediumTests')) { 784 $result['timeoutForMediumTests'] = $this->getInteger( 785 (string) $root->getAttribute('timeoutForMediumTests'), 786 10 787 ); 788 } 789 790 if ($root->hasAttribute('timeoutForLargeTests')) { 791 $result['timeoutForLargeTests'] = $this->getInteger( 792 (string) $root->getAttribute('timeoutForLargeTests'), 793 60 794 ); 795 } 796 797 if ($root->hasAttribute('reverseDefectList')) { 798 $result['reverseDefectList'] = $this->getBoolean( 799 (string) $root->getAttribute('reverseDefectList'), 800 false 801 ); 802 } 803 804 if ($root->hasAttribute('verbose')) { 805 $result['verbose'] = $this->getBoolean( 806 (string) $root->getAttribute('verbose'), 807 false 808 ); 809 } 810 811 if ($root->hasAttribute('registerMockObjectsFromTestArgumentsRecursively')) { 812 $result['registerMockObjectsFromTestArgumentsRecursively'] = $this->getBoolean( 813 (string) $root->getAttribute('registerMockObjectsFromTestArgumentsRecursively'), 814 false 815 ); 816 } 817 818 if ($root->hasAttribute('extensionsDirectory')) { 819 $result['extensionsDirectory'] = $this->toAbsolutePath( 820 (string) $root->getAttribute( 821 'extensionsDirectory' 822 ) 823 ); 824 } 825 826 return $result; 827 } 828 829 /** 830 * Returns the test suite configuration. 831 * 832 * @return PHPUnit_Framework_TestSuite 833 */ 834 public function getTestSuiteConfiguration($testSuiteFilter = null) 835 { 836 $testSuiteNodes = $this->xpath->query('testsuites/testsuite'); 837 838 if ($testSuiteNodes->length == 0) { 839 $testSuiteNodes = $this->xpath->query('testsuite'); 840 } 841 842 if ($testSuiteNodes->length == 1) { 843 return $this->getTestSuite($testSuiteNodes->item(0), $testSuiteFilter); 844 } 845 846 if ($testSuiteNodes->length > 1) { 847 $suite = new PHPUnit_Framework_TestSuite; 848 849 foreach ($testSuiteNodes as $testSuiteNode) { 850 $suite->addTestSuite( 851 $this->getTestSuite($testSuiteNode, $testSuiteFilter) 852 ); 853 } 854 855 return $suite; 856 } 857 } 858 859 /** 860 * Returns the test suite names from the configuration. 861 * 862 * @return array 863 */ 864 public function getTestSuiteNames() 865 { 866 $names = []; 867 $nodes = $this->xpath->query('*/testsuite'); 868 foreach ($nodes as $node) { 869 $names[] = $node->getAttribute('name'); 870 } 871 872 return $names; 873 } 874 875 /** 876 * @param DOMElement $testSuiteNode 877 * 878 * @return PHPUnit_Framework_TestSuite 879 */ 880 protected function getTestSuite(DOMElement $testSuiteNode, $testSuiteFilter = null) 881 { 882 if ($testSuiteNode->hasAttribute('name')) { 883 $suite = new PHPUnit_Framework_TestSuite( 884 (string) $testSuiteNode->getAttribute('name') 885 ); 886 } else { 887 $suite = new PHPUnit_Framework_TestSuite; 888 } 889 890 $exclude = []; 891 892 foreach ($testSuiteNode->getElementsByTagName('exclude') as $excludeNode) { 893 $excludeFile = (string) $excludeNode->textContent; 894 if ($excludeFile) { 895 $exclude[] = $this->toAbsolutePath($excludeFile); 896 } 897 } 898 899 $fileIteratorFacade = new File_Iterator_Facade; 900 901 foreach ($testSuiteNode->getElementsByTagName('directory') as $directoryNode) { 902 if ($testSuiteFilter && $directoryNode->parentNode->getAttribute('name') != $testSuiteFilter) { 903 continue; 904 } 905 906 $directory = (string) $directoryNode->textContent; 907 908 if (empty($directory)) { 909 continue; 910 } 911 912 if ($directoryNode->hasAttribute('phpVersion')) { 913 $phpVersion = (string) $directoryNode->getAttribute('phpVersion'); 914 } else { 915 $phpVersion = PHP_VERSION; 916 } 917 918 if ($directoryNode->hasAttribute('phpVersionOperator')) { 919 $phpVersionOperator = (string) $directoryNode->getAttribute('phpVersionOperator'); 920 } else { 921 $phpVersionOperator = '>='; 922 } 923 924 if (!version_compare(PHP_VERSION, $phpVersion, $phpVersionOperator)) { 925 continue; 926 } 927 928 if ($directoryNode->hasAttribute('prefix')) { 929 $prefix = (string) $directoryNode->getAttribute('prefix'); 930 } else { 931 $prefix = ''; 932 } 933 934 if ($directoryNode->hasAttribute('suffix')) { 935 $suffix = (string) $directoryNode->getAttribute('suffix'); 936 } else { 937 $suffix = 'Test.php'; 938 } 939 940 $files = $fileIteratorFacade->getFilesAsArray( 941 $this->toAbsolutePath($directory), 942 $suffix, 943 $prefix, 944 $exclude 945 ); 946 $suite->addTestFiles($files); 947 } 948 949 foreach ($testSuiteNode->getElementsByTagName('file') as $fileNode) { 950 if ($testSuiteFilter && $fileNode->parentNode->getAttribute('name') != $testSuiteFilter) { 951 continue; 952 } 953 954 $file = (string) $fileNode->textContent; 955 956 if (empty($file)) { 957 continue; 958 } 959 960 // Get the absolute path to the file 961 $file = $fileIteratorFacade->getFilesAsArray( 962 $this->toAbsolutePath($file) 963 ); 964 965 if (!isset($file[0])) { 966 continue; 967 } 968 969 $file = $file[0]; 970 971 if ($fileNode->hasAttribute('phpVersion')) { 972 $phpVersion = (string) $fileNode->getAttribute('phpVersion'); 973 } else { 974 $phpVersion = PHP_VERSION; 975 } 976 977 if ($fileNode->hasAttribute('phpVersionOperator')) { 978 $phpVersionOperator = (string) $fileNode->getAttribute('phpVersionOperator'); 979 } else { 980 $phpVersionOperator = '>='; 981 } 982 983 if (!version_compare(PHP_VERSION, $phpVersion, $phpVersionOperator)) { 984 continue; 985 } 986 987 $suite->addTestFile($file); 988 } 989 990 return $suite; 991 } 992 993 /** 994 * @param string $value 995 * @param bool $default 996 * 997 * @return bool 998 */ 999 protected function getBoolean($value, $default) 1000 { 1001 if (strtolower($value) == 'false') { 1002 return false; 1003 } elseif (strtolower($value) == 'true') { 1004 return true; 1005 } 1006 1007 return $default; 1008 } 1009 1010 /** 1011 * @param string $value 1012 * @param bool $default 1013 * 1014 * @return bool 1015 */ 1016 protected function getInteger($value, $default) 1017 { 1018 if (is_numeric($value)) { 1019 return (int) $value; 1020 } 1021 1022 return $default; 1023 } 1024 1025 /** 1026 * @param string $query 1027 * 1028 * @return array 1029 */ 1030 protected function readFilterDirectories($query) 1031 { 1032 $directories = []; 1033 1034 foreach ($this->xpath->query($query) as $directory) { 1035 $directoryPath = (string) $directory->textContent; 1036 1037 if (!$directoryPath) { 1038 continue; 1039 } 1040 1041 if ($directory->hasAttribute('prefix')) { 1042 $prefix = (string) $directory->getAttribute('prefix'); 1043 } else { 1044 $prefix = ''; 1045 } 1046 1047 if ($directory->hasAttribute('suffix')) { 1048 $suffix = (string) $directory->getAttribute('suffix'); 1049 } else { 1050 $suffix = '.php'; 1051 } 1052 1053 if ($directory->hasAttribute('group')) { 1054 $group = (string) $directory->getAttribute('group'); 1055 } else { 1056 $group = 'DEFAULT'; 1057 } 1058 1059 $directories[] = [ 1060 'path' => $this->toAbsolutePath($directoryPath), 1061 'prefix' => $prefix, 1062 'suffix' => $suffix, 1063 'group' => $group 1064 ]; 1065 } 1066 1067 return $directories; 1068 } 1069 1070 /** 1071 * @param string $query 1072 * 1073 * @return array 1074 */ 1075 protected function readFilterFiles($query) 1076 { 1077 $files = []; 1078 1079 foreach ($this->xpath->query($query) as $file) { 1080 $filePath = (string) $file->textContent; 1081 1082 if ($filePath) { 1083 $files[] = $this->toAbsolutePath($filePath); 1084 } 1085 } 1086 1087 return $files; 1088 } 1089 1090 /** 1091 * @param string $path 1092 * @param bool $useIncludePath 1093 * 1094 * @return string 1095 */ 1096 protected function toAbsolutePath($path, $useIncludePath = false) 1097 { 1098 $path = trim($path); 1099 1100 if ($path[0] === '/') { 1101 return $path; 1102 } 1103 1104 // Matches the following on Windows: 1105 // - \\NetworkComputer\Path 1106 // - \\.\D: 1107 // - \\.\c: 1108 // - C:\Windows 1109 // - C:\windows 1110 // - C:/windows 1111 // - c:/windows 1112 if (defined('PHP_WINDOWS_VERSION_BUILD') && 1113 ($path[0] === '\\' || 1114 (strlen($path) >= 3 && preg_match('#^[A-Z]\:[/\\\]#i', substr($path, 0, 3))))) { 1115 return $path; 1116 } 1117 1118 // Stream 1119 if (strpos($path, '://') !== false) { 1120 return $path; 1121 } 1122 1123 $file = dirname($this->filename) . DIRECTORY_SEPARATOR . $path; 1124 1125 if ($useIncludePath && !file_exists($file)) { 1126 $includePathFile = stream_resolve_include_path($path); 1127 1128 if ($includePathFile) { 1129 $file = $includePathFile; 1130 } 1131 } 1132 1133 return $file; 1134 } 1135} 1136