5 * Copyright (c) 2002-2011, Sebastian Bergmann <sebastian@phpunit.de>.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * * Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
20 * * Neither the name of Sebastian Bergmann nor the names of his
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
39 * @author Sebastian Bergmann <sebastian@phpunit.de>
40 * @copyright 2002-2011 Sebastian Bergmann <sebastian@phpunit.de>
41 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
42 * @link http://www.phpunit.de/
43 * @since File available since Release 3.2.0
47 * Wrapper for the PHPUnit XML configuration file.
49 * Example XML configuration file:
51 * <?xml version="1.0" encoding="utf-8" ?>
53 * <phpunit backupGlobals="true"
54 * backupStaticAttributes="false"
55 * bootstrap="/path/to/bootstrap.php"
57 * convertErrorsToExceptions="true"
58 * convertNoticesToExceptions="true"
59 * convertWarningsToExceptions="true"
60 * forceCoversAnnotation="false"
61 * mapTestClassNameToCoveredClassName="false"
62 * processIsolation="false"
64 * stopOnFailure="false"
65 * stopOnIncomplete="false"
66 * stopOnSkipped="false"
68 * testSuiteLoaderClass="PHPUnit_Runner_StandardTestSuiteLoader"
72 * <testsuite name="My Test Suite">
73 * <directory suffix="Test.php">/path/to/files</directory>
74 * <file>/path/to/MyTest.php</file>
89 * <directory suffix=".php">/path/to/files</directory>
90 * <file>/path/to/file</file>
92 * <directory suffix=".php">/path/to/files</directory>
93 * <file>/path/to/file</file>
96 * <whitelist addUncoveredFilesFromWhitelist="true">
97 * <directory suffix=".php">/path/to/files</directory>
98 * <file>/path/to/file</file>
100 * <directory suffix=".php">/path/to/files</directory>
101 * <file>/path/to/file</file>
107 * <listener class="MyListener" file="/optional/path/to/MyListener.php">
111 * <string>Sebastian</string>
114 * <integer>22</integer>
115 * <string>April</string>
116 * <double>19.78</double>
118 * <object class="stdClass"/>
119 * <file>MyRelativeFile.php</file>
120 * <directory>MyRelativeDir</directory>
126 * <log type="coverage-html" target="/tmp/report" title="My Project"
127 charset="UTF-8" yui="true" highlight="false"
128 * lowUpperBound="35" highLowerBound="70"/>
129 * <log type="coverage-clover" target="/tmp/clover.xml"/>
130 * <log type="json" target="/tmp/logfile.json"/>
131 * <log type="plain" target="/tmp/logfile.txt"/>
132 * <log type="tap" target="/tmp/logfile.tap"/>
133 * <log type="junit" target="/tmp/logfile.xml" logIncompleteSkipped="false"/>
134 * <log type="story-html" target="/tmp/story.html"/>
135 * <log type="story-text" target="/tmp/story.txt"/>
136 * <log type="testdox-html" target="/tmp/testdox.html"/>
137 * <log type="testdox-text" target="/tmp/testdox.txt"/>
141 * <includePath>.</includePath>
142 * <ini name="foo" value="bar"/>
143 * <const name="foo" value="bar"/>
144 * <var name="foo" value="bar"/>
145 * <env name="foo" value="bar"/>
146 * <post name="foo" value="bar"/>
147 * <get name="foo" value="bar"/>
148 * <cookie name="foo" value="bar"/>
149 * <server name="foo" value="bar"/>
150 * <files name="foo" value="bar"/>
151 * <request name="foo" value="bar"/>
155 * <browser name="Firefox on Linux"
156 * browser="*firefox /usr/lib/firefox/firefox-bin"
157 * host="my.linux.box"
166 * @author Sebastian Bergmann <sebastian@phpunit.de>
167 * @copyright 2002-2011 Sebastian Bergmann <sebastian@phpunit.de>
168 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
169 * @version Release: 3.5.14
170 * @link http://www.phpunit.de/
171 * @since Class available since Release 3.2.0
173 class PHPUnit_Util_Configuration
175 private static $instances = array();
182 * Loads a PHPUnit configuration file.
184 * @param string $filename
186 protected function __construct($filename)
188 $this->filename = $filename;
189 $this->document = PHPUnit_Util_XML::loadFile($filename);
190 $this->xpath = new DOMXPath($this->document);
194 * @since Method available since Release 3.4.0
196 private final function __clone()
201 * Returns a PHPUnit configuration object.
203 * @param string $filename
204 * @return PHPUnit_Util_Configuration
205 * @since Method available since Release 3.4.0
207 public static function getInstance($filename)
209 $realpath = realpath($filename);
211 if ($realpath === FALSE) {
212 throw new PHPUnit_Framework_Exception(
214 'Could not read "%s".',
220 if (!isset(self::$instances[$realpath])) {
221 self::$instances[$realpath] = new PHPUnit_Util_Configuration($realpath);
224 return self::$instances[$realpath];
228 * Returns the configuration for SUT filtering.
231 * @since Method available since Release 3.2.1
233 public function getFilterConfiguration()
235 $addUncoveredFilesFromWhitelist = TRUE;
237 $tmp = $this->xpath->query('filter/whitelist');
239 if ($tmp->length == 1 &&
240 $tmp->item(0)->hasAttribute('addUncoveredFilesFromWhitelist')) {
241 $addUncoveredFilesFromWhitelist = $this->getBoolean(
242 (string)$tmp->item(0)->getAttribute('addUncoveredFilesFromWhitelist'),
248 'blacklist' => array(
250 'directory' => $this->readFilterDirectories(
251 'filter/blacklist/directory'
253 'file' => $this->readFilterFiles(
254 'filter/blacklist/file'
258 'directory' => $this->readFilterDirectories(
259 'filter/blacklist/exclude/directory'
261 'file' => $this->readFilterFiles(
262 'filter/blacklist/exclude/file'
266 'whitelist' => array(
267 'addUncoveredFilesFromWhitelist' => $addUncoveredFilesFromWhitelist,
269 'directory' => $this->readFilterDirectories(
270 'filter/whitelist/directory'
272 'file' => $this->readFilterFiles(
273 'filter/whitelist/file'
277 'directory' => $this->readFilterDirectories(
278 'filter/whitelist/exclude/directory'
280 'file' => $this->readFilterFiles(
281 'filter/whitelist/exclude/file'
289 * Returns the configuration for groups.
292 * @since Method available since Release 3.2.1
294 public function getGroupConfiguration()
297 'include' => array(),
301 foreach ($this->xpath->query('groups/include/group') as $group) {
302 $groups['include'][] = (string)$group->nodeValue;
305 foreach ($this->xpath->query('groups/exclude/group') as $group) {
306 $groups['exclude'][] = (string)$group->nodeValue;
313 * Returns the configuration for listeners.
316 * @since Method available since Release 3.4.0
318 public function getListenerConfiguration()
322 foreach ($this->xpath->query('listeners/listener') as $listener) {
323 $class = (string)$listener->getAttribute('class');
325 $arguments = array();
327 if ($listener->hasAttribute('file')) {
328 $file = $this->toAbsolutePath((string)$listener->getAttribute('file'));
331 if ($listener->childNodes->item(1) instanceof DOMElement &&
332 $listener->childNodes->item(1)->tagName == 'arguments') {
333 foreach ($listener->childNodes->item(1)->childNodes as $argument) {
334 if ($argument instanceof DOMElement) {
335 if($argument->tagName == 'file' || $argument->tagName == 'directory') {
336 $arguments[] = $this->toAbsolutePath((string)$argument->nodeValue);
338 $arguments[] = PHPUnit_Util_XML::xmlToVariable($argument);
347 'arguments' => $arguments
355 * Returns the logging configuration.
359 public function getLoggingConfiguration()
363 foreach ($this->xpath->query('logging/log') as $log) {
364 $type = (string)$log->getAttribute('type');
365 $target = $this->toAbsolutePath((string)$log->getAttribute('target'));
367 if ($type == 'coverage-html') {
368 if ($log->hasAttribute('title')) {
369 $result['title'] = (string)$log->getAttribute('title');
372 if ($log->hasAttribute('charset')) {
373 $result['charset'] = (string)$log->getAttribute('charset');
376 if ($log->hasAttribute('lowUpperBound')) {
377 $result['lowUpperBound'] = (string)$log->getAttribute('lowUpperBound');
380 if ($log->hasAttribute('highLowerBound')) {
381 $result['highLowerBound'] = (string)$log->getAttribute('highLowerBound');
384 if ($log->hasAttribute('yui')) {
385 $result['yui'] = $this->getBoolean(
386 (string)$log->getAttribute('yui'),
391 if ($log->hasAttribute('highlight')) {
392 $result['highlight'] = $this->getBoolean(
393 (string)$log->getAttribute('highlight'),
399 else if ($type == 'junit') {
400 if ($log->hasAttribute('logIncompleteSkipped')) {
401 $result['logIncompleteSkipped'] = $this->getBoolean(
402 (string)$log->getAttribute('logIncompleteSkipped'),
408 $result[$type] = $target;
415 * Returns the PHP configuration.
418 * @since Method available since Release 3.2.1
420 public function getPHPConfiguration()
423 'include_path' => '',
436 $nl = $this->xpath->query('php/includePath');
438 if ($nl->length == 1) {
439 $result['include_path'] = $this->toAbsolutePath((string)$nl->item(0)->nodeValue);
442 foreach ($this->xpath->query('php/ini') as $ini) {
443 $name = (string)$ini->getAttribute('name');
444 $value = (string)$ini->getAttribute('value');
446 $result['ini'][$name] = $value;
449 foreach ($this->xpath->query('php/const') as $const) {
450 $name = (string)$const->getAttribute('name');
451 $value = (string)$const->getAttribute('value');
453 $result['const'][$name] = $this->getBoolean($value, $value);
456 foreach (array('var', 'env', 'post', 'get', 'cookie', 'server', 'files', 'request') as $array) {
457 foreach ($this->xpath->query('php/' . $array) as $var) {
458 $name = (string)$var->getAttribute('name');
459 $value = (string)$var->getAttribute('value');
461 $result[$array][$name] = $this->getBoolean($value, $value);
469 * Handles the PHP configuration.
471 * @since Method available since Release 3.2.20
473 public function handlePHPConfiguration()
475 $configuration = $this->getPHPConfiguration();
477 if ($configuration['include_path'] != '') {
480 $configuration['include_path'] . PATH_SEPARATOR .
481 ini_get('include_path')
485 foreach ($configuration['ini'] as $name => $value) {
486 if (defined($value)) {
487 $value = constant($value);
490 ini_set($name, $value);
493 foreach ($configuration['const'] as $name => $value) {
494 if (!defined($name)) {
495 define($name, $value);
499 foreach (array('var', 'env', 'post', 'get', 'cookie', 'server', 'files', 'request') as $array) {
500 if ($array == 'var') {
503 $target = &$GLOBALS['_' . strtoupper($array)];
506 foreach ($configuration[$array] as $name => $value) {
507 $target[$name] = $value;
513 * Returns the PHPUnit configuration.
516 * @since Method available since Release 3.2.14
518 public function getPHPUnitConfiguration()
521 $root = $this->document->documentElement;
523 if ($root->hasAttribute('colors')) {
524 $result['colors'] = $this->getBoolean(
525 (string)$root->getAttribute('colors'), FALSE
529 if ($root->hasAttribute('backupGlobals')) {
530 $result['backupGlobals'] = $this->getBoolean(
531 (string)$root->getAttribute('backupGlobals'), TRUE
535 if ($root->hasAttribute('backupStaticAttributes')) {
536 $result['backupStaticAttributes'] = $this->getBoolean(
537 (string)$root->getAttribute('backupStaticAttributes'), FALSE
541 if ($root->hasAttribute('bootstrap')) {
542 $result['bootstrap'] = $this->toAbsolutePath(
543 (string)$root->getAttribute('bootstrap')
547 if ($root->hasAttribute('convertErrorsToExceptions')) {
548 $result['convertErrorsToExceptions'] = $this->getBoolean(
549 (string)$root->getAttribute('convertErrorsToExceptions'), TRUE
553 if ($root->hasAttribute('convertNoticesToExceptions')) {
554 $result['convertNoticesToExceptions'] = $this->getBoolean(
555 (string)$root->getAttribute('convertNoticesToExceptions'), TRUE
559 if ($root->hasAttribute('convertWarningsToExceptions')) {
560 $result['convertWarningsToExceptions'] = $this->getBoolean(
561 (string)$root->getAttribute('convertWarningsToExceptions'), TRUE
565 if ($root->hasAttribute('forceCoversAnnotation')) {
566 $result['forceCoversAnnotation'] = $this->getBoolean(
567 (string)$root->getAttribute('forceCoversAnnotation'), FALSE
571 if ($root->hasAttribute('mapTestClassNameToCoveredClassName')) {
572 $result['mapTestClassNameToCoveredClassName'] = $this->getBoolean(
573 (string)$root->getAttribute('mapTestClassNameToCoveredClassName'),
578 if ($root->hasAttribute('processIsolation')) {
579 $result['processIsolation'] = $this->getBoolean(
580 (string)$root->getAttribute('processIsolation'), FALSE
584 if ($root->hasAttribute('stopOnError')) {
585 $result['stopOnError'] = $this->getBoolean(
586 (string)$root->getAttribute('stopOnError'), FALSE
590 if ($root->hasAttribute('stopOnFailure')) {
591 $result['stopOnFailure'] = $this->getBoolean(
592 (string)$root->getAttribute('stopOnFailure'), FALSE
596 if ($root->hasAttribute('stopOnIncomplete')) {
597 $result['stopOnIncomplete'] = $this->getBoolean(
598 (string)$root->getAttribute('stopOnIncomplete'), FALSE
602 if ($root->hasAttribute('stopOnSkipped')) {
603 $result['stopOnSkipped'] = $this->getBoolean(
604 (string)$root->getAttribute('stopOnSkipped'), FALSE
608 if ($root->hasAttribute('syntaxCheck')) {
609 $result['syntaxCheck'] = $this->getBoolean(
610 (string)$root->getAttribute('syntaxCheck'), FALSE
614 if ($root->hasAttribute('testSuiteLoaderClass')) {
615 $result['testSuiteLoaderClass'] = (string)$root->getAttribute(
616 'testSuiteLoaderClass'
620 if ($root->hasAttribute('testSuiteLoaderFile')) {
621 $result['testSuiteLoaderFile'] = (string)$root->getAttribute(
622 'testSuiteLoaderFile'
626 if ($root->hasAttribute('strict')) {
627 $result['strict'] = $this->getBoolean(
628 (string)$root->getAttribute('strict'), FALSE
632 if ($root->hasAttribute('verbose')) {
633 $result['verbose'] = $this->getBoolean(
634 (string)$root->getAttribute('verbose'), FALSE
642 * Returns the SeleniumTestCase browser configuration.
645 * @since Method available since Release 3.2.9
647 public function getSeleniumBrowserConfiguration()
651 foreach ($this->xpath->query('selenium/browser') as $config) {
652 $name = (string)$config->getAttribute('name');
653 $browser = (string)$config->getAttribute('browser');
655 if ($config->hasAttribute('host')) {
656 $host = (string)$config->getAttribute('host');
661 if ($config->hasAttribute('port')) {
662 $port = (int)$config->getAttribute('port');
667 if ($config->hasAttribute('timeout')) {
668 $timeout = (int)$config->getAttribute('timeout');
675 'browser' => $browser,
678 'timeout' => $timeout
686 * Returns the test suite configuration.
688 * @param boolean $syntaxCheck
689 * @return PHPUnit_Framework_TestSuite
690 * @since Method available since Release 3.2.1
692 public function getTestSuiteConfiguration($syntaxCheck = FALSE)
694 $testSuiteNodes = $this->xpath->query('testsuites/testsuite');
696 if ($testSuiteNodes->length == 0) {
697 $testSuiteNodes = $this->xpath->query('testsuite');
700 if ($testSuiteNodes->length == 1) {
701 return $this->getTestSuite($testSuiteNodes->item(0), $syntaxCheck);
704 if ($testSuiteNodes->length > 1) {
705 $suite = new PHPUnit_Framework_TestSuite;
707 foreach ($testSuiteNodes as $testSuiteNode) {
708 $suite->addTestSuite(
709 $this->getTestSuite($testSuiteNode, $syntaxCheck)
718 * @param DOMElement $testSuiteNode
719 * @param boolean $syntaxCheck
720 * @return PHPUnit_Framework_TestSuite
721 * @since Method available since Release 3.4.0
723 protected function getTestSuite(DOMElement $testSuiteNode, $syntaxCheck)
725 if ($testSuiteNode->hasAttribute('name')) {
726 $suite = new PHPUnit_Framework_TestSuite(
727 (string)$testSuiteNode->getAttribute('name')
730 $suite = new PHPUnit_Framework_TestSuite;
733 foreach ($testSuiteNode->getElementsByTagName('directory') as $directoryNode) {
734 $directory = (string)$directoryNode->nodeValue;
736 if (empty($directory)) {
740 if ($directoryNode->hasAttribute('prefix')) {
741 $prefix = (string)$directoryNode->getAttribute('prefix');
746 if ($directoryNode->hasAttribute('suffix')) {
747 $suffix = (string)$directoryNode->getAttribute('suffix');
749 $suffix = 'Test.php';
752 $testCollector = new PHPUnit_Runner_IncludePathTestCollector(
753 array($this->toAbsolutePath($directory)), $suffix, $prefix
756 $suite->addTestFiles($testCollector->collectTests(), $syntaxCheck);
759 foreach ($testSuiteNode->getElementsByTagName('file') as $fileNode) {
760 $file = (string)$fileNode->nodeValue;
766 $suite->addTestFile($file, $syntaxCheck);
773 * @param string $value
774 * @param boolean $default
776 * @since Method available since Release 3.2.3
778 protected function getBoolean($value, $default)
780 if (strtolower($value) == 'false') {
784 else if (strtolower($value) == 'true') {
792 * @param string $query
794 * @since Method available since Release 3.2.3
796 protected function readFilterDirectories($query)
798 $directories = array();
800 foreach ($this->xpath->query($query) as $directory) {
801 if ($directory->hasAttribute('prefix')) {
802 $prefix = (string)$directory->getAttribute('prefix');
807 if ($directory->hasAttribute('suffix')) {
808 $suffix = (string)$directory->getAttribute('suffix');
813 if ($directory->hasAttribute('group')) {
814 $group = (string)$directory->getAttribute('group');
819 $directories[] = array(
820 'path' => $this->toAbsolutePath((string)$directory->nodeValue),
831 * @param string $query
833 * @since Method available since Release 3.2.3
835 protected function readFilterFiles($query)
839 foreach ($this->xpath->query($query) as $file) {
840 $files[] = $this->toAbsolutePath((string)$file->nodeValue);
847 * @param string $path
849 * @since Method available since Release 3.5.0
851 protected function toAbsolutePath($path)
853 // is the path already an absolute path?
854 if ($path[0] === '/' || $path[0] === '\\' ||
855 (strlen($path) > 3 && ctype_alpha($path[0]) &&
856 $path[1] === ':' && ($path[2] === '\\' || $path[2] === '/'))) {
860 return dirname($this->filename) . DIRECTORY_SEPARATOR . $path;