3 * This file is part of PHPUnit.
5 * (c) Sebastian Bergmann <sebastian@phpunit.de>
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
12 * A TestResult collects the results of executing a test case.
14 * @since Class available since Release 2.0.0
16 class PHPUnit_Framework_TestResult implements Countable
21 protected $passed = array();
26 protected $errors = array();
31 protected $failures = array();
36 protected $notImplemented = array();
41 protected $risky = array();
46 protected $skipped = array();
51 protected $listeners = array();
56 protected $runTests = 0;
64 * @var PHPUnit_Framework_TestSuite
66 protected $topTestSuite = null;
69 * Code Coverage information.
71 * @var PHP_CodeCoverage
73 protected $codeCoverage;
78 protected $convertErrorsToExceptions = true;
83 protected $stop = false;
88 protected $stopOnError = false;
93 protected $stopOnFailure = false;
98 protected $beStrictAboutTestsThatDoNotTestAnything = false;
103 protected $beStrictAboutOutputDuringTests = false;
108 protected $beStrictAboutTestSize = false;
113 protected $beStrictAboutTodoAnnotatedTests = false;
118 protected $stopOnRisky = false;
123 protected $stopOnIncomplete = false;
128 protected $stopOnSkipped = false;
133 protected $lastTestFailed = false;
138 protected $timeoutForSmallTests = 1;
143 protected $timeoutForMediumTests = 10;
148 protected $timeoutForLargeTests = 60;
151 * Registers a TestListener.
153 * @param PHPUnit_Framework_TestListener
155 public function addListener(PHPUnit_Framework_TestListener $listener)
157 $this->listeners[] = $listener;
161 * Unregisters a TestListener.
163 * @param PHPUnit_Framework_TestListener $listener
165 public function removeListener(PHPUnit_Framework_TestListener $listener)
167 foreach ($this->listeners as $key => $_listener) {
168 if ($listener === $_listener) {
169 unset($this->listeners[$key]);
175 * Flushes all flushable TestListeners.
177 * @since Method available since Release 3.0.0
179 public function flushListeners()
181 foreach ($this->listeners as $listener) {
182 if ($listener instanceof PHPUnit_Util_Printer) {
189 * Adds an error to the list of errors.
191 * @param PHPUnit_Framework_Test $test
192 * @param Exception $e
195 public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
197 if ($e instanceof PHPUnit_Framework_RiskyTest) {
198 $this->risky[] = new PHPUnit_Framework_TestFailure($test, $e);
199 $notifyMethod = 'addRiskyTest';
201 if ($this->stopOnRisky) {
204 } elseif ($e instanceof PHPUnit_Framework_IncompleteTest) {
205 $this->notImplemented[] = new PHPUnit_Framework_TestFailure($test, $e);
206 $notifyMethod = 'addIncompleteTest';
208 if ($this->stopOnIncomplete) {
211 } elseif ($e instanceof PHPUnit_Framework_SkippedTest) {
212 $this->skipped[] = new PHPUnit_Framework_TestFailure($test, $e);
213 $notifyMethod = 'addSkippedTest';
215 if ($this->stopOnSkipped) {
219 $this->errors[] = new PHPUnit_Framework_TestFailure($test, $e);
220 $notifyMethod = 'addError';
222 if ($this->stopOnError || $this->stopOnFailure) {
227 foreach ($this->listeners as $listener) {
228 $listener->$notifyMethod($test, $e, $time);
231 $this->lastTestFailed = true;
232 $this->time += $time;
236 * Adds a failure to the list of failures.
237 * The passed in exception caused the failure.
239 * @param PHPUnit_Framework_Test $test
240 * @param PHPUnit_Framework_AssertionFailedError $e
243 public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
245 if ($e instanceof PHPUnit_Framework_RiskyTest ||
246 $e instanceof PHPUnit_Framework_OutputError) {
247 $this->risky[] = new PHPUnit_Framework_TestFailure($test, $e);
248 $notifyMethod = 'addRiskyTest';
250 if ($this->stopOnRisky) {
253 } elseif ($e instanceof PHPUnit_Framework_IncompleteTest) {
254 $this->notImplemented[] = new PHPUnit_Framework_TestFailure($test, $e);
255 $notifyMethod = 'addIncompleteTest';
257 if ($this->stopOnIncomplete) {
260 } elseif ($e instanceof PHPUnit_Framework_SkippedTest) {
261 $this->skipped[] = new PHPUnit_Framework_TestFailure($test, $e);
262 $notifyMethod = 'addSkippedTest';
264 if ($this->stopOnSkipped) {
268 $this->failures[] = new PHPUnit_Framework_TestFailure($test, $e);
269 $notifyMethod = 'addFailure';
271 if ($this->stopOnFailure) {
276 foreach ($this->listeners as $listener) {
277 $listener->$notifyMethod($test, $e, $time);
280 $this->lastTestFailed = true;
281 $this->time += $time;
285 * Informs the result that a testsuite will be started.
287 * @param PHPUnit_Framework_TestSuite $suite
289 * @since Method available since Release 2.2.0
291 public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
293 if ($this->topTestSuite === null) {
294 $this->topTestSuite = $suite;
297 foreach ($this->listeners as $listener) {
298 $listener->startTestSuite($suite);
303 * Informs the result that a testsuite was completed.
305 * @param PHPUnit_Framework_TestSuite $suite
307 * @since Method available since Release 2.2.0
309 public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
311 foreach ($this->listeners as $listener) {
312 $listener->endTestSuite($suite);
317 * Informs the result that a test will be started.
319 * @param PHPUnit_Framework_Test $test
321 public function startTest(PHPUnit_Framework_Test $test)
323 $this->lastTestFailed = false;
324 $this->runTests += count($test);
326 foreach ($this->listeners as $listener) {
327 $listener->startTest($test);
332 * Informs the result that a test was completed.
334 * @param PHPUnit_Framework_Test $test
337 public function endTest(PHPUnit_Framework_Test $test, $time)
339 foreach ($this->listeners as $listener) {
340 $listener->endTest($test, $time);
343 if (!$this->lastTestFailed && $test instanceof PHPUnit_Framework_TestCase) {
344 $class = get_class($test);
345 $key = $class . '::' . $test->getName();
347 $this->passed[$key] = array(
348 'result' => $test->getResult(),
349 'size' => PHPUnit_Util_Test::getSize(
351 $test->getName(false)
355 $this->time += $time;
360 * Returns true if no risky test occurred.
364 * @since Method available since Release 4.0.0
366 public function allHarmless()
368 return $this->riskyCount() == 0;
372 * Gets the number of risky tests.
376 * @since Method available since Release 4.0.0
378 public function riskyCount()
380 return count($this->risky);
384 * Returns true if no incomplete test occurred.
388 public function allCompletelyImplemented()
390 return $this->notImplementedCount() == 0;
394 * Gets the number of incomplete tests.
398 public function notImplementedCount()
400 return count($this->notImplemented);
404 * Returns an Enumeration for the risky tests.
408 * @since Method available since Release 4.0.0
410 public function risky()
416 * Returns an Enumeration for the incomplete tests.
420 public function notImplemented()
422 return $this->notImplemented;
426 * Returns true if no test has been skipped.
430 * @since Method available since Release 3.0.0
432 public function noneSkipped()
434 return $this->skippedCount() == 0;
438 * Gets the number of skipped tests.
442 * @since Method available since Release 3.0.0
444 public function skippedCount()
446 return count($this->skipped);
450 * Returns an Enumeration for the skipped tests.
454 * @since Method available since Release 3.0.0
456 public function skipped()
458 return $this->skipped;
462 * Gets the number of detected errors.
466 public function errorCount()
468 return count($this->errors);
472 * Returns an Enumeration for the errors.
476 public function errors()
478 return $this->errors;
482 * Gets the number of detected failures.
486 public function failureCount()
488 return count($this->failures);
492 * Returns an Enumeration for the failures.
496 public function failures()
498 return $this->failures;
502 * Returns the names of the tests that have passed.
506 * @since Method available since Release 3.4.0
508 public function passed()
510 return $this->passed;
514 * Returns the (top) test suite.
516 * @return PHPUnit_Framework_TestSuite
518 * @since Method available since Release 3.0.0
520 public function topTestSuite()
522 return $this->topTestSuite;
526 * Returns whether code coverage information should be collected.
528 * @return bool If code coverage should be collected
530 * @since Method available since Release 3.2.0
532 public function getCollectCodeCoverageInformation()
534 return $this->codeCoverage !== null;
540 * @param PHPUnit_Framework_Test $test
542 public function run(PHPUnit_Framework_Test $test)
544 PHPUnit_Framework_Assert::resetCount();
552 $this->startTest($test);
554 $errorHandlerSet = false;
556 if ($this->convertErrorsToExceptions) {
557 $oldErrorHandler = set_error_handler(
558 array('PHPUnit_Util_ErrorHandler', 'handleError'),
562 if ($oldErrorHandler === null) {
563 $errorHandlerSet = true;
565 restore_error_handler();
569 $collectCodeCoverage = $this->codeCoverage !== null &&
570 !$test instanceof PHPUnit_Extensions_SeleniumTestCase &&
571 !$test instanceof PHPUnit_Framework_Warning;
573 if ($collectCodeCoverage) {
574 // We need to blacklist test source files when no whitelist is used.
575 if (!$this->codeCoverage->filter()->hasWhitelist()) {
576 $classes = $this->getHierarchy(get_class($test), true);
578 foreach ($classes as $class) {
579 $this->codeCoverage->filter()->addFileToBlacklist(
580 $class->getFileName()
585 $this->codeCoverage->start($test);
591 if (!$test instanceof PHPUnit_Framework_Warning &&
592 $test->getSize() != PHPUnit_Util_Test::UNKNOWN &&
593 $this->beStrictAboutTestSize &&
594 extension_loaded('pcntl') && class_exists('PHP_Invoker')) {
595 switch ($test->getSize()) {
596 case PHPUnit_Util_Test::SMALL:
597 $_timeout = $this->timeoutForSmallTests;
600 case PHPUnit_Util_Test::MEDIUM:
601 $_timeout = $this->timeoutForMediumTests;
604 case PHPUnit_Util_Test::LARGE:
605 $_timeout = $this->timeoutForLargeTests;
609 $invoker = new PHP_Invoker;
610 $invoker->invoke(array($test, 'runBare'), array(), $_timeout);
614 } catch (PHPUnit_Framework_AssertionFailedError $e) {
617 if ($e instanceof PHPUnit_Framework_RiskyTestError) {
619 } elseif ($e instanceof PHPUnit_Framework_IncompleteTestError) {
621 } elseif ($e instanceof PHPUnit_Framework_SkippedTestError) {
624 } catch (PHPUnit_Framework_Exception $e) {
626 } catch (Throwable $e) {
627 $e = new PHPUnit_Framework_ExceptionWrapper($e);
629 } catch (Exception $e) {
630 $e = new PHPUnit_Framework_ExceptionWrapper($e);
634 $time = PHP_Timer::stop();
635 $test->addToAssertionCount(PHPUnit_Framework_Assert::getCount());
637 if ($this->beStrictAboutTestsThatDoNotTestAnything &&
638 $test->getNumAssertions() == 0) {
642 if ($collectCodeCoverage) {
643 $append = !$risky && !$incomplete && !$skipped;
644 $linesToBeCovered = array();
645 $linesToBeUsed = array();
647 if ($append && $test instanceof PHPUnit_Framework_TestCase) {
648 $linesToBeCovered = PHPUnit_Util_Test::getLinesToBeCovered(
650 $test->getName(false)
653 $linesToBeUsed = PHPUnit_Util_Test::getLinesToBeUsed(
655 $test->getName(false)
660 $this->codeCoverage->stop(
665 } catch (PHP_CodeCoverage_Exception_UnintentionallyCoveredCode $cce) {
668 new PHPUnit_Framework_UnintentionallyCoveredCodeError(
669 'This test executed code that is not listed as code to be covered or used:' .
670 PHP_EOL . $cce->getMessage()
674 } catch (PHPUnit_Framework_InvalidCoversTargetException $cce) {
677 new PHPUnit_Framework_InvalidCoversTargetError(
682 } catch (PHP_CodeCoverage_Exception $cce) {
691 if ($errorHandlerSet === true) {
692 restore_error_handler();
695 if ($error === true) {
696 $this->addError($test, $e, $time);
697 } elseif ($failure === true) {
698 $this->addFailure($test, $e, $time);
699 } elseif ($this->beStrictAboutTestsThatDoNotTestAnything &&
700 $test->getNumAssertions() == 0) {
703 new PHPUnit_Framework_RiskyTestError(
704 'This test did not perform any assertions'
708 } elseif ($this->beStrictAboutOutputDuringTests && $test->hasOutput()) {
711 new PHPUnit_Framework_OutputError(
713 'This test printed output: %s',
714 $test->getActualOutput()
719 } elseif ($this->beStrictAboutTodoAnnotatedTests && $test instanceof PHPUnit_Framework_TestCase) {
720 $annotations = $test->getAnnotations();
722 if (isset($annotations['method']['todo'])) {
725 new PHPUnit_Framework_RiskyTestError(
726 'Test method is annotated with @todo'
733 $this->endTest($test, $time);
737 * Gets the number of run tests.
741 public function count()
743 return $this->runTests;
747 * Checks whether the test run should stop.
751 public function shouldStop()
757 * Marks that the test run should stop.
759 public function stop()
765 * Returns the PHP_CodeCoverage object.
767 * @return PHP_CodeCoverage
769 * @since Method available since Release 3.5.0
771 public function getCodeCoverage()
773 return $this->codeCoverage;
777 * Sets the PHP_CodeCoverage object.
779 * @param PHP_CodeCoverage $codeCoverage
781 * @since Method available since Release 3.6.0
783 public function setCodeCoverage(PHP_CodeCoverage $codeCoverage)
785 $this->codeCoverage = $codeCoverage;
789 * Enables or disables the error-to-exception conversion.
793 * @throws PHPUnit_Framework_Exception
795 * @since Method available since Release 3.2.14
797 public function convertErrorsToExceptions($flag)
799 if (!is_bool($flag)) {
800 throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
803 $this->convertErrorsToExceptions = $flag;
807 * Returns the error-to-exception conversion setting.
811 * @since Method available since Release 3.4.0
813 public function getConvertErrorsToExceptions()
815 return $this->convertErrorsToExceptions;
819 * Enables or disables the stopping when an error occurs.
823 * @throws PHPUnit_Framework_Exception
825 * @since Method available since Release 3.5.0
827 public function stopOnError($flag)
829 if (!is_bool($flag)) {
830 throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
833 $this->stopOnError = $flag;
837 * Enables or disables the stopping when a failure occurs.
841 * @throws PHPUnit_Framework_Exception
843 * @since Method available since Release 3.1.0
845 public function stopOnFailure($flag)
847 if (!is_bool($flag)) {
848 throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
851 $this->stopOnFailure = $flag;
857 * @throws PHPUnit_Framework_Exception
859 * @since Method available since Release 4.0.0
861 public function beStrictAboutTestsThatDoNotTestAnything($flag)
863 if (!is_bool($flag)) {
864 throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
867 $this->beStrictAboutTestsThatDoNotTestAnything = $flag;
873 * @since Method available since Release 4.0.0
875 public function isStrictAboutTestsThatDoNotTestAnything()
877 return $this->beStrictAboutTestsThatDoNotTestAnything;
883 * @throws PHPUnit_Framework_Exception
885 * @since Method available since Release 4.0.0
887 public function beStrictAboutOutputDuringTests($flag)
889 if (!is_bool($flag)) {
890 throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
893 $this->beStrictAboutOutputDuringTests = $flag;
899 * @since Method available since Release 4.0.0
901 public function isStrictAboutOutputDuringTests()
903 return $this->beStrictAboutOutputDuringTests;
909 * @throws PHPUnit_Framework_Exception
911 * @since Method available since Release 4.0.0
913 public function beStrictAboutTestSize($flag)
915 if (!is_bool($flag)) {
916 throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
919 $this->beStrictAboutTestSize = $flag;
925 * @since Method available since Release 4.0.0
927 public function isStrictAboutTestSize()
929 return $this->beStrictAboutTestSize;
935 * @throws PHPUnit_Framework_Exception
937 * @since Method available since Release 4.2.0
939 public function beStrictAboutTodoAnnotatedTests($flag)
941 if (!is_bool($flag)) {
942 throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
945 $this->beStrictAboutTodoAnnotatedTests = $flag;
951 * @since Method available since Release 4.2.0
953 public function isStrictAboutTodoAnnotatedTests()
955 return $this->beStrictAboutTodoAnnotatedTests;
959 * Enables or disables the stopping for risky tests.
963 * @throws PHPUnit_Framework_Exception
965 * @since Method available since Release 4.0.0
967 public function stopOnRisky($flag)
969 if (!is_bool($flag)) {
970 throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
973 $this->stopOnRisky = $flag;
977 * Enables or disables the stopping for incomplete tests.
981 * @throws PHPUnit_Framework_Exception
983 * @since Method available since Release 3.5.0
985 public function stopOnIncomplete($flag)
987 if (!is_bool($flag)) {
988 throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
991 $this->stopOnIncomplete = $flag;
995 * Enables or disables the stopping for skipped tests.
999 * @throws PHPUnit_Framework_Exception
1001 * @since Method available since Release 3.1.0
1003 public function stopOnSkipped($flag)
1005 if (!is_bool($flag)) {
1006 throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'boolean');
1009 $this->stopOnSkipped = $flag;
1013 * Returns the time spent running the tests.
1017 public function time()
1023 * Returns whether the entire test was successful or not.
1027 public function wasSuccessful()
1029 return empty($this->errors) && empty($this->failures);
1033 * Sets the timeout for small tests.
1035 * @param int $timeout
1037 * @throws PHPUnit_Framework_Exception
1039 * @since Method available since Release 3.6.0
1041 public function setTimeoutForSmallTests($timeout)
1043 if (!is_integer($timeout)) {
1044 throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer');
1047 $this->timeoutForSmallTests = $timeout;
1051 * Sets the timeout for medium tests.
1053 * @param int $timeout
1055 * @throws PHPUnit_Framework_Exception
1057 * @since Method available since Release 3.6.0
1059 public function setTimeoutForMediumTests($timeout)
1061 if (!is_integer($timeout)) {
1062 throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer');
1065 $this->timeoutForMediumTests = $timeout;
1069 * Sets the timeout for large tests.
1071 * @param int $timeout
1073 * @throws PHPUnit_Framework_Exception
1075 * @since Method available since Release 3.6.0
1077 public function setTimeoutForLargeTests($timeout)
1079 if (!is_integer($timeout)) {
1080 throw PHPUnit_Util_InvalidArgumentHelper::factory(1, 'integer');
1083 $this->timeoutForLargeTests = $timeout;
1087 * Returns the class hierarchy for a given class.
1089 * @param string $className
1090 * @param bool $asReflectionObjects
1094 protected function getHierarchy($className, $asReflectionObjects = false)
1096 if ($asReflectionObjects) {
1097 $classes = array(new ReflectionClass($className));
1099 $classes = array($className);
1105 if ($asReflectionObjects) {
1106 $class = new ReflectionClass(
1107 $classes[count($classes) - 1]->getName()
1110 $class = new ReflectionClass($classes[count($classes) - 1]);
1113 $parent = $class->getParentClass();
1115 if ($parent !== false) {
1116 if ($asReflectionObjects) {
1117 $classes[] = $parent;
1119 $classes[] = $parent->getName();