5
* Copyright (c) 2002-2010, Sebastian Bergmann <sb@sebastian-bergmann.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 <sb@sebastian-bergmann.de>
40
* @copyright 2002-2010 Sebastian Bergmann <sb@sebastian-bergmann.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.0.0
46
require_once 'PHPUnit/TextUI/TestRunner.php';
47
require_once 'PHPUnit/Util/Configuration.php';
48
require_once 'PHPUnit/Util/Fileloader.php';
49
require_once 'PHPUnit/Util/Filesystem.php';
50
require_once 'PHPUnit/Util/Filter.php';
51
require_once 'PHPUnit/Util/Getopt.php';
53
PHPUnit_Util_Filter::addFileToFilter(__FILE__, 'PHPUNIT');
56
* A TestRunner for the Command Line Interface (CLI)
61
* @author Sebastian Bergmann <sb@sebastian-bergmann.de>
62
* @copyright 2002-2010 Sebastian Bergmann <sb@sebastian-bergmann.de>
63
* @license http://www.opensource.org/licenses/bsd-license.php BSD License
64
* @version Release: 3.4.14
65
* @link http://www.phpunit.de/
66
* @since Class available since Release 3.0.0
68
class PHPUnit_TextUI_Command
73
protected $arguments = array(
74
'listGroups' => FALSE,
76
'syntaxCheck' => FALSE,
77
'useDefaultConfiguration' => TRUE
83
protected $options = array();
88
protected $longOptions = array(
92
'configuration=' => NULL,
93
'coverage-html=' => NULL,
94
'coverage-clover=' => NULL,
95
'coverage-source=' => NULL,
96
'coverage-xml=' => NULL,
98
'exclude-group=' => NULL,
102
'include-path=' => NULL,
103
'list-groups' => NULL,
105
'log-graphviz=' => NULL,
107
'log-junit=' => NULL,
108
'log-metrics=' => NULL,
112
'process-isolation' => NULL,
116
'skeleton-class' => NULL,
117
'skeleton-test' => NULL,
119
'stop-on-failure' => NULL,
121
'story-html=' => NULL,
122
'story-text=' => NULL,
123
'syntax-check' => NULL,
125
'test-db-dsn=' => NULL,
126
'test-db-log-rev=' => NULL,
127
'test-db-log-prefix=' => NULL,
128
'test-db-log-info=' => NULL,
130
'testdox-html=' => NULL,
131
'testdox-text=' => NULL,
132
'no-configuration' => NULL,
133
'no-globals-backup' => NULL,
134
'static-backup' => NULL,
141
* @param boolean $exit
143
public static function main($exit = TRUE)
145
$command = new PHPUnit_TextUI_Command;
146
$command->run($_SERVER['argv'], $exit);
151
* @param boolean $exit
153
public function run(array $argv, $exit = TRUE)
155
$this->handleArguments($argv);
157
$runner = new PHPUnit_TextUI_TestRunner($this->arguments['loader']);
159
if (is_object($this->arguments['test']) &&
160
$this->arguments['test'] instanceof PHPUnit_Framework_Test) {
161
$suite = $this->arguments['test'];
163
$suite = $runner->getTest(
164
$this->arguments['test'],
165
$this->arguments['testFile'],
166
$this->arguments['syntaxCheck']
170
if ($suite->testAt(0) instanceof PHPUnit_Framework_Warning &&
171
strpos($suite->testAt(0)->getMessage(), 'No tests found in class') !== FALSE) {
172
$message = $suite->testAt(0)->getMessage();
173
$start = strpos($message, '"') + 1;
174
$end = strpos($message, '"', $start);
175
$className = substr($message, $start, $end - $start);
177
require_once 'PHPUnit/Util/Skeleton/Test.php';
179
$skeleton = new PHPUnit_Util_Skeleton_Test(
181
$this->arguments['testFile']
184
$result = $skeleton->generate(TRUE);
186
if (!$result['incomplete']) {
187
eval(str_replace(array('<?php', '?>'), '', $result['code']));
188
$suite = new PHPUnit_Framework_TestSuite(
189
$this->arguments['test'] . 'Test'
194
if ($this->arguments['listGroups']) {
195
PHPUnit_TextUI_TestRunner::printVersionString();
197
print "Available test group(s):\n";
199
$groups = $suite->getGroups();
202
foreach ($groups as $group) {
206
exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT);
209
unset($this->arguments['test']);
210
unset($this->arguments['testFile']);
213
$result = $runner->doRun($suite, $this->arguments);
216
catch (PHPUnit_Framework_Exception $e) {
217
print $e->getMessage() . "\n";
221
if (isset($result) && $result->wasSuccessful()) {
222
exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT);
225
else if (!isset($result) || $result->errorCount() > 0) {
226
exit(PHPUnit_TextUI_TestRunner::EXCEPTION_EXIT);
230
exit(PHPUnit_TextUI_TestRunner::FAILURE_EXIT);
236
* Handles the command-line arguments.
238
* A child class of PHPUnit_TextUI_Command can hook into the argument
239
* parsing by adding the switch(es) to the $longOptions array and point to a
240
* callback method that handles the switch(es) in the child class like this
244
* class MyCommand extends PHPUnit_TextUI_Command
246
* public function __construct()
248
* $this->longOptions['--my-switch'] = 'myHandler';
251
* // --my-switch foo -> myHandler('foo')
252
* protected function myHandler($value)
260
protected function handleArguments(array $argv)
263
$this->options = PHPUnit_Util_Getopt::getopt(
266
array_keys($this->longOptions)
270
catch (RuntimeException $e) {
271
PHPUnit_TextUI_TestRunner::showError($e->getMessage());
274
$skeletonClass = FALSE;
275
$skeletonTest = FALSE;
277
foreach ($this->options[0] as $option) {
278
switch ($option[0]) {
281
'The --ansi option is deprecated, please use --colors ' .
288
$this->arguments['colors'] = TRUE;
292
case '--bootstrap': {
293
$this->arguments['bootstrap'] = $option[1];
297
case '--configuration': {
298
$this->arguments['configuration'] = $option[1];
302
case '--coverage-xml': {
304
'The --coverage-xml option is deprecated, please use ' .
305
'--coverage-clover instead.',
310
case '--coverage-clover': {
311
if (extension_loaded('tokenizer') &&
312
extension_loaded('xdebug')) {
313
$this->arguments['coverageClover'] = $option[1];
315
if (!extension_loaded('tokenizer')) {
317
'The tokenizer extension is not loaded.'
321
'The Xdebug extension is not loaded.'
328
case '--coverage-source': {
329
if (extension_loaded('tokenizer') &&
330
extension_loaded('xdebug')) {
331
$this->arguments['coverageSource'] = $option[1];
333
if (!extension_loaded('tokenizer')) {
335
'The tokenizer extension is not loaded.'
339
'The Xdebug extension is not loaded.'
348
'The --report option is deprecated, please use ' .
349
'--coverage-html instead.',
354
case '--coverage-html': {
355
if (extension_loaded('tokenizer') &&
356
extension_loaded('xdebug')) {
357
$this->arguments['reportDirectory'] = $option[1];
359
if (!extension_loaded('tokenizer')) {
361
'The tokenizer extension is not loaded.'
365
'The Xdebug extension is not loaded.'
373
$ini = explode('=', $option[1]);
375
if (isset($ini[0])) {
376
if (isset($ini[1])) {
377
ini_set($ini[0], $ini[1]);
379
ini_set($ini[0], TRUE);
386
$this->arguments['debug'] = TRUE;
392
exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT);
397
$this->arguments['filter'] = $option[1];
402
$this->arguments['groups'] = explode(',', $option[1]);
406
case '--exclude-group': {
407
$this->arguments['excludeGroups'] = explode(
413
case '--include-path': {
414
$includePath = $option[1];
418
case '--list-groups': {
419
$this->arguments['listGroups'] = TRUE;
424
$this->arguments['loader'] = $option[1];
429
$this->arguments['jsonLogfile'] = $option[1];
435
'The --log-xml option is deprecated, please use ' .
436
'--log-junit instead.',
441
case '--log-junit': {
442
$this->arguments['junitLogfile'] = $option[1];
446
case '--log-graphviz': {
448
'The --log-graphviz functionality is deprecated and ' .
449
'will be removed in the future.',
453
if (PHPUnit_Util_Filesystem::fileExistsInIncludePath('Image/GraphViz.php')) {
454
$this->arguments['graphvizLogfile'] = $option[1];
457
'The Image_GraphViz package is not installed.'
464
$this->arguments['tapLogfile'] = $option[1];
470
'The --log-pmd functionality is deprecated and will be ' .
471
'removed in the future.',
475
if (extension_loaded('tokenizer') &&
476
extension_loaded('xdebug')) {
477
$this->arguments['pmdXML'] = $option[1];
479
if (!extension_loaded('tokenizer')) {
481
'The tokenizer extension is not loaded.'
485
'The Xdebug extension is not loaded.'
492
case '--log-metrics': {
494
'The --log-metrics functionality is deprecated and ' .
495
'will be removed in the future.',
499
if (extension_loaded('tokenizer') &&
500
extension_loaded('xdebug')) {
501
$this->arguments['metricsXML'] = $option[1];
503
if (!extension_loaded('tokenizer')) {
505
'The tokenizer extension is not loaded.'
509
'The Xdebug extension is not loaded.'
516
case '--process-isolation': {
517
$this->arguments['processIsolation'] = TRUE;
518
$this->arguments['syntaxCheck'] = FALSE;
523
$this->arguments['repeat'] = (int)$option[1];
528
$this->arguments['printer'] = new PHPUnit_TextUI_ResultPrinter(
530
isset($this->arguments['verbose']) ? $this->arguments['verbose'] : FALSE
535
case '--stop-on-failure': {
536
$this->arguments['stopOnFailure'] = TRUE;
540
case '--test-db-dsn': {
542
'The test database functionality is deprecated and ' .
543
'will be removed in the future.',
547
if (extension_loaded('pdo')) {
548
$this->arguments['testDatabaseDSN'] = $option[1];
550
$this->showMessage('The PDO extension is not loaded.');
555
case '--test-db-log-rev': {
556
if (extension_loaded('pdo')) {
557
$this->arguments['testDatabaseLogRevision'] = $option[1];
559
$this->showMessage('The PDO extension is not loaded.');
564
case '--test-db-prefix': {
565
if (extension_loaded('pdo')) {
566
$this->arguments['testDatabasePrefix'] = $option[1];
568
$this->showMessage('The PDO extension is not loaded.');
573
case '--test-db-log-info': {
574
if (extension_loaded('pdo')) {
575
$this->arguments['testDatabaseLogInfo'] = $option[1];
577
$this->showMessage('The PDO extension is not loaded.');
584
'The --skeleton option is deprecated, please use ' .
585
'--skeleton-test instead.',
590
case '--skeleton-test': {
591
$skeletonTest = TRUE;
592
$skeletonClass = FALSE;
596
case '--skeleton-class': {
597
$skeletonClass = TRUE;
598
$skeletonTest = FALSE;
603
require_once 'PHPUnit/Util/Log/TAP.php';
605
$this->arguments['printer'] = new PHPUnit_Util_Log_TAP;
610
require_once 'PHPUnit/Extensions/Story/ResultPrinter/Text.php';
612
$this->arguments['printer'] = new PHPUnit_Extensions_Story_ResultPrinter_Text;
616
case '--story-html': {
617
$this->arguments['storyHTMLFile'] = $option[1];
621
case '--story-text': {
622
$this->arguments['storyTextFile'] = $option[1];
626
case '--syntax-check': {
627
$this->arguments['syntaxCheck'] = TRUE;
632
require_once 'PHPUnit/Util/TestDox/ResultPrinter/Text.php';
634
$this->arguments['printer'] = new PHPUnit_Util_TestDox_ResultPrinter_Text;
638
case '--testdox-html': {
639
$this->arguments['testdoxHTMLFile'] = $option[1];
643
case '--testdox-text': {
644
$this->arguments['testdoxTextFile'] = $option[1];
648
case '--no-configuration': {
649
$this->arguments['useDefaultConfiguration'] = FALSE;
653
case '--no-globals-backup': {
654
$this->arguments['backupGlobals'] = FALSE;
658
case '--static-backup': {
659
$this->arguments['backupStaticAttributes'] = TRUE;
664
$this->arguments['verbose'] = TRUE;
669
PHPUnit_TextUI_TestRunner::printVersionString();
670
exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT);
675
$this->arguments['wait'] = TRUE;
680
$optionName = str_replace('--', '', $option[0]);
682
if (isset($this->longOptions[$optionName])) {
683
$handler = $this->longOptions[$optionName];
686
else if (isset($this->longOptions[$optionName . '='])) {
687
$handler = $this->longOptions[$optionName . '='];
690
if (isset($handler) && is_callable(array($this, $handler))) {
691
$this->$handler($option[1]);
697
if (isset($this->arguments['printer']) &&
698
$this->arguments['printer'] instanceof PHPUnit_Extensions_Story_ResultPrinter_Text &&
699
isset($this->arguments['processIsolation']) &&
700
$this->arguments['processIsolation']) {
702
'The story result printer cannot be used in process isolation.'
706
$this->handleCustomTestSuite();
708
if (!isset($this->arguments['test'])) {
709
if (isset($this->options[1][0])) {
710
$this->arguments['test'] = $this->options[1][0];
713
if (isset($this->options[1][1])) {
714
$this->arguments['testFile'] = $this->options[1][1];
716
$this->arguments['testFile'] = '';
719
if (isset($this->arguments['test']) && is_file($this->arguments['test'])) {
720
$this->arguments['testFile'] = realpath($this->arguments['test']);
721
$this->arguments['test'] = substr($this->arguments['test'], 0, strrpos($this->arguments['test'], '.'));
725
if (isset($includePath)) {
728
$includePath . PATH_SEPARATOR . ini_get('include_path')
732
if (isset($this->arguments['bootstrap'])) {
733
PHPUnit_Util_Fileloader::load($this->arguments['bootstrap']);
736
if ($this->arguments['loader'] !== NULL) {
737
$this->arguments['loader'] = $this->handleLoader($this->arguments['loader']);
740
if (!isset($this->arguments['configuration']) && $this->arguments['useDefaultConfiguration']) {
741
if (file_exists('phpunit.xml')) {
742
$this->arguments['configuration'] = realpath('phpunit.xml');
745
else if (file_exists('phpunit.xml.dist')) {
746
$this->arguments['configuration'] = realpath('phpunit.xml.dist');
750
if (isset($this->arguments['configuration'])) {
751
$configuration = PHPUnit_Util_Configuration::getInstance(
752
$this->arguments['configuration']
755
$phpunit = $configuration->getPHPUnitConfiguration();
757
if (isset($phpunit['syntaxCheck'])) {
758
$this->arguments['syntaxCheck'] = $phpunit['syntaxCheck'];
761
if (isset($phpunit['testSuiteLoaderClass'])) {
762
if (isset($phpunit['testSuiteLoaderFile'])) {
763
$file = $phpunit['testSuiteLoaderFile'];
768
$this->arguments['loader'] = $this->handleLoader(
769
$phpunit['testSuiteLoaderClass'], $file
773
$configuration->handlePHPConfiguration();
775
if (!isset($this->arguments['bootstrap'])) {
776
$phpunitConfiguration = $configuration->getPHPUnitConfiguration();
778
if (isset($phpunitConfiguration['bootstrap'])) {
779
PHPUnit_Util_Fileloader::load($phpunitConfiguration['bootstrap']);
783
$browsers = $configuration->getSeleniumBrowserConfiguration();
785
if (!empty($browsers)) {
786
require_once 'PHPUnit/Extensions/SeleniumTestCase.php';
787
PHPUnit_Extensions_SeleniumTestCase::$browsers = $browsers;
790
if (!isset($this->arguments['test'])) {
791
$testSuite = $configuration->getTestSuiteConfiguration(
792
$this->arguments['syntaxCheck']
795
if ($testSuite !== NULL) {
796
$this->arguments['test'] = $testSuite;
801
if (isset($this->arguments['test']) && is_string($this->arguments['test']) && substr($this->arguments['test'], -5, 5) == '.phpt') {
802
require_once 'PHPUnit/Extensions/PhptTestCase.php';
804
$test = new PHPUnit_Extensions_PhptTestCase($this->arguments['test']);
806
$this->arguments['test'] = new PHPUnit_Framework_TestSuite;
807
$this->arguments['test']->addTest($test);
810
if (!isset($this->arguments['test']) ||
811
(isset($this->arguments['testDatabaseLogRevision']) && !isset($this->arguments['testDatabaseDSN']))) {
813
exit(PHPUnit_TextUI_TestRunner::EXCEPTION_EXIT);
816
if (!isset($this->arguments['syntaxCheck'])) {
817
$this->arguments['syntaxCheck'] = FALSE;
820
if ($skeletonClass || $skeletonTest) {
821
if (isset($this->arguments['test']) && $this->arguments['test'] !== FALSE) {
822
PHPUnit_TextUI_TestRunner::printVersionString();
824
if ($skeletonClass) {
825
require_once 'PHPUnit/Util/Skeleton/Class.php';
827
$class = 'PHPUnit_Util_Skeleton_Class';
829
require_once 'PHPUnit/Util/Skeleton/Test.php';
831
$class = 'PHPUnit_Util_Skeleton_Test';
836
$reflector = new ReflectionClass($class);
838
for ($i = 0; $i <= 3; $i++) {
839
if (isset($this->options[1][$i])) {
840
$args[] = $this->options[1][$i];
844
$skeleton = $reflector->newInstanceArgs($args);
848
catch (Exception $e) {
849
print $e->getMessage() . "\n";
850
exit(PHPUnit_TextUI_TestRunner::FAILURE_EXIT);
854
'Wrote skeleton for "%s" to "%s".' . "\n",
855
$skeleton->getOutClassName(),
856
$skeleton->getOutSourceFile()
859
exit(PHPUnit_TextUI_TestRunner::SUCCESS_EXIT);
862
exit(PHPUnit_TextUI_TestRunner::EXCEPTION_EXIT);
868
* Handles the loading of the PHPUnit_Runner_TestSuiteLoader implementation.
870
* @param string $loaderClass
871
* @param string $loaderFile
873
protected function handleLoader($loaderClass, $loaderFile = '')
875
if (!class_exists($loaderClass, FALSE)) {
876
if ($loaderFile == '') {
877
$loaderFile = PHPUnit_Util_Filesystem::classNameToFilename(
882
$loaderFile = PHPUnit_Util_Filesystem::fileExistsInIncludePath(
886
if ($loaderFile !== FALSE) {
891
if (class_exists($loaderClass, FALSE)) {
892
$class = new ReflectionClass($loaderClass);
894
if ($class->implementsInterface('PHPUnit_Runner_TestSuiteLoader') &&
895
$class->isInstantiable()) {
896
$loader = $class->newInstance();
900
if (!isset($loader)) {
901
PHPUnit_TextUI_TestRunner::showError(
903
'Could not use "%s" as loader.',
916
* @param string $message
917
* @param boolean $exit
919
protected function showMessage($message, $exit = TRUE)
921
PHPUnit_TextUI_TestRunner::printVersionString();
922
print $message . "\n";
925
exit(PHPUnit_TextUI_TestRunner::EXCEPTION_EXIT);
932
* Show the help message.
934
protected function showHelp()
936
PHPUnit_TextUI_TestRunner::printVersionString();
939
Usage: phpunit [switches] UnitTest [UnitTest.php]
940
phpunit [switches] <directory>
942
--log-junit <file> Log test execution in JUnit XML format to file.
943
--log-tap <file> Log test execution in TAP format to file.
944
--log-json <file> Log test execution in JSON format.
946
--coverage-html <dir> Generate code coverage report in HTML format.
947
--coverage-clover <file> Write code coverage data in Clover XML format.
948
--coverage-source <dir> Write code coverage / source data in XML format.
950
--story-html <file> Write Story/BDD results in HTML format to file.
951
--story-text <file> Write Story/BDD results in Text format to file.
953
--testdox-html <file> Write agile documentation in HTML format to file.
954
--testdox-text <file> Write agile documentation in Text format to file.
956
--filter <pattern> Filter which tests to run.
957
--group ... Only runs tests from the specified group(s).
958
--exclude-group ... Exclude tests from the specified group(s).
959
--list-groups List available test groups.
961
--loader <loader> TestSuiteLoader implementation to use.
962
--repeat <times> Runs the test(s) repeatedly.
964
--story Report test execution progress in Story/BDD format.
965
--tap Report test execution progress in TAP format.
966
--testdox Report test execution progress in TestDox format.
968
--colors Use colors in output.
969
--stderr Write to STDERR instead of STDOUT.
970
--stop-on-failure Stop execution upon first error or failure.
971
--verbose Output more verbose information.
972
--wait Waits for a keystroke after each test.
974
--skeleton-class Generate Unit class for UnitTest in UnitTest.php.
975
--skeleton-test Generate UnitTest class for Unit in Unit.php.
977
--process-isolation Run each test in a separate PHP process.
978
--no-globals-backup Do not backup and restore \$GLOBALS for each test.
979
--static-backup Backup and restore static attributes for each test.
980
--syntax-check Try to check source files for syntax errors.
982
--bootstrap <file> A "bootstrap" PHP file that is run before the tests.
983
--configuration <file> Read configuration from XML file.
984
--no-configuration Ignore default configuration file (phpunit.xml).
985
--include-path <path(s)> Prepend PHP's include_path with given path(s).
986
-d key[=value] Sets a php.ini value.
988
--help Prints this usage information.
989
--version Prints the version and exits.
995
* Custom callback for test suite discovery.
997
protected function handleCustomTestSuite()
b'\\ No newline at end of file'