5 * Copyright (c) 2002-2009, 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-2009 Sebastian Bergmann <sb@sebastian-bergmann.de>
41 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
43 * @link http://www.phpunit.de/
44 * @since File available since Release 2.0.0
47 require_once 'PHPUnit/Framework.php';
48 require_once 'PHPUnit/Util/CodeCoverage.php';
49 require_once 'PHPUnit/Util/ErrorHandler.php';
50 require_once 'PHPUnit/Util/Filter.php';
51 require_once 'PHPUnit/Util/Printer.php';
52 require_once 'PHPUnit/Util/Test.php';
53 require_once 'PHPUnit/Util/Timer.php';
55 PHPUnit_Util_Filter::addFileToFilter(__FILE__, 'PHPUNIT');
57 if (!class_exists('PHPUnit_Framework_TestResult', FALSE)) {
60 * A TestResult collects the results of executing a test case.
64 * @author Sebastian Bergmann <sb@sebastian-bergmann.de>
65 * @copyright 2002-2009 Sebastian Bergmann <sb@sebastian-bergmann.de>
66 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
67 * @version Release: 3.3.17
68 * @link http://www.phpunit.de/
69 * @since Class available since Release 2.0.0
71 class PHPUnit_Framework_TestResult implements Countable
73 protected static $xdebugLoaded = NULL;
74 protected static $useXdebug = NULL;
79 protected $errors = array();
84 protected $failures = array();
89 protected $notImplemented = array();
94 protected $skipped = array();
99 protected $listeners = array();
104 protected $runTests = 0;
112 * @var PHPUnit_Framework_TestSuite
114 protected $topTestSuite = NULL;
117 * Code Coverage information provided by Xdebug.
121 protected $codeCoverageInformation = array();
126 protected $collectCodeCoverageInformation = FALSE;
131 protected $convertErrorsToExceptions = TRUE;
136 protected $stop = FALSE;
141 protected $stopOnFailure = FALSE;
146 protected $lastTestFailed = FALSE;
149 * Registers a TestListener.
151 * @param PHPUnit_Framework_TestListener
153 public function addListener(PHPUnit_Framework_TestListener $listener)
155 $this->listeners[] = $listener;
159 * Unregisters a TestListener.
161 * @param PHPUnit_Framework_TestListener $listener
163 public function removeListener(PHPUnit_Framework_TestListener $listener)
165 foreach ($this->listeners as $key => $_listener) {
166 if ($listener === $_listener) {
167 unset($this->listeners[$key]);
173 * Flushes all flushable TestListeners.
175 * @since Method available since Release 3.0.0
177 public function flushListeners()
179 foreach ($this->listeners as $listener) {
180 if ($listener instanceof PHPUnit_Util_Printer) {
187 * Adds an error to the list of errors.
188 * The passed in exception caused the error.
190 * @param PHPUnit_Framework_Test $test
191 * @param Exception $e
194 public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
196 if ($e instanceof PHPUnit_Framework_IncompleteTest) {
197 $this->notImplemented[] = new PHPUnit_Framework_TestFailure($test, $e);
198 $notifyMethod = 'addIncompleteTest';
201 else if ($e instanceof PHPUnit_Framework_SkippedTest) {
202 $this->skipped[] = new PHPUnit_Framework_TestFailure($test, $e);
203 $notifyMethod = 'addSkippedTest';
207 $this->errors[] = new PHPUnit_Framework_TestFailure($test, $e);
208 $notifyMethod = 'addError';
210 if ($this->stopOnFailure) {
215 foreach ($this->listeners as $listener) {
216 $listener->$notifyMethod($test, $e, $time);
219 $this->lastTestFailed = TRUE;
220 $this->time += $time;
224 * Adds a failure to the list of failures.
225 * The passed in exception caused the failure.
227 * @param PHPUnit_Framework_Test $test
228 * @param PHPUnit_Framework_AssertionFailedError $e
231 public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
233 if ($e instanceof PHPUnit_Framework_IncompleteTest) {
234 $this->notImplemented[] = new PHPUnit_Framework_TestFailure($test, $e);
235 $notifyMethod = 'addIncompleteTest';
238 else if ($e instanceof PHPUnit_Framework_SkippedTest) {
239 $this->skipped[] = new PHPUnit_Framework_TestFailure($test, $e);
240 $notifyMethod = 'addSkippedTest';
244 $this->failures[] = new PHPUnit_Framework_TestFailure($test, $e);
245 $notifyMethod = 'addFailure';
247 if ($this->stopOnFailure) {
252 foreach ($this->listeners as $listener) {
253 $listener->$notifyMethod($test, $e, $time);
256 $this->lastTestFailed = TRUE;
257 $this->time += $time;
261 * Informs the result that a testsuite will be started.
263 * @param PHPUnit_Framework_TestSuite $suite
264 * @since Method available since Release 2.2.0
266 public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
268 if ($this->topTestSuite === NULL) {
269 $this->topTestSuite = $suite;
272 foreach ($this->listeners as $listener) {
273 $listener->startTestSuite($suite);
278 * Informs the result that a testsuite was completed.
280 * @param PHPUnit_Framework_TestSuite $suite
281 * @since Method available since Release 2.2.0
283 public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
285 foreach ($this->listeners as $listener) {
286 $listener->endTestSuite($suite);
291 * Informs the result that a test will be started.
293 * @param PHPUnit_Framework_Test $test
295 public function startTest(PHPUnit_Framework_Test $test)
297 $this->lastTestFailed = FALSE;
298 $this->runTests += count($test);
300 foreach ($this->listeners as $listener) {
301 $listener->startTest($test);
306 * Informs the result that a test was completed.
308 * @param PHPUnit_Framework_Test $test
311 public function endTest(PHPUnit_Framework_Test $test, $time)
313 foreach ($this->listeners as $listener) {
314 $listener->endTest($test, $time);
317 if (!$this->lastTestFailed) {
318 $this->time += $time;
323 * Returns TRUE if no incomplete test occured.
327 public function allCompletlyImplemented()
329 return $this->notImplementedCount() == 0;
333 * Gets the number of incomplete tests.
337 public function notImplementedCount()
339 return count($this->notImplemented);
343 * Returns an Enumeration for the incomplete tests.
347 public function notImplemented()
349 return $this->notImplemented;
353 * Returns TRUE if no test has been skipped.
356 * @since Method available since Release 3.0.0
358 public function noneSkipped()
360 return $this->skippedCount() == 0;
364 * Gets the number of skipped tests.
367 * @since Method available since Release 3.0.0
369 public function skippedCount()
371 return count($this->skipped);
375 * Returns an Enumeration for the skipped tests.
378 * @since Method available since Release 3.0.0
380 public function skipped()
382 return $this->skipped;
386 * Gets the number of detected errors.
390 public function errorCount()
392 return count($this->errors);
396 * Returns an Enumeration for the errors.
400 public function errors()
402 return $this->errors;
406 * Gets the number of detected failures.
410 public function failureCount()
412 return count($this->failures);
416 * Returns an Enumeration for the failures.
420 public function failures()
422 return $this->failures;
426 * Returns the (top) test suite.
428 * @return PHPUnit_Framework_TestSuite
429 * @since Method available since Release 3.0.0
431 public function topTestSuite()
433 return $this->topTestSuite;
437 * Enables or disables the collection of Code Coverage information.
439 * @param boolean $flag
440 * @throws InvalidArgumentException
441 * @since Method available since Release 2.3.0
443 public function collectCodeCoverageInformation($flag)
445 if (is_bool($flag)) {
446 $this->collectCodeCoverageInformation = $flag;
448 throw new InvalidArgumentException;
453 * Returns whether code coverage information should be collected.
455 * @return boolean If code coverage should be collected
456 * @since Method available since Release 3.2.0
458 public function getCollectCodeCoverageInformation()
460 return $this->collectCodeCoverageInformation;
464 * Appends code coverage information to the test
466 * @param PHPUnit_Framework_Test $test
468 * @since Method available since Release 3.2.0
470 public function appendCodeCoverageInformation(PHPUnit_Framework_Test $test, $data)
473 $executableCode = array();
475 foreach (array_keys($data) as $file) {
476 if (PHPUnit_Util_Filter::isFiltered($file, FALSE)) {
481 $newFilesToCollect = array_diff_key($data, PHPUnit_Util_Filter::getCoveredFiles());
483 if (count($newFilesToCollect) > 0) {
484 $deadCode = PHPUnit_Util_CodeCoverage::getDeadLines($newFilesToCollect);
485 $executableCode = PHPUnit_Util_CodeCoverage::getExecutableLines($newFilesToCollect);
487 foreach (array_keys($newFilesToCollect) as $file) {
488 PHPUnit_Util_Filter::addCoveredFile($file);
491 unset($newFilesToCollect);
494 if ($test instanceof PHPUnit_Framework_TestCase) {
495 $linesToBeCovered = PHPUnit_Util_Test::getLinesToBeCovered(
496 get_class($test), $test->getName()
499 if (!empty($linesToBeCovered)) {
500 $data = array_intersect_key($data, $linesToBeCovered);
502 foreach (array_keys($data) as $file) {
503 $data[$file] = array_intersect_key($data[$file], array_flip($linesToBeCovered[$file]));
508 $executed = PHPUnit_Util_CodeCoverage::getExecutedLines($data);
511 $this->codeCoverageInformation[] = array(
513 'files' => $executed,
515 'executable' => $executableCode,
520 * Returns Code Coverage data per test case.
522 * Format of the result array:
527 * 'test' => PHPUnit_Framework_Test
529 * "/tested/code.php" => array(
537 * flag < 0: Line is executable but was not executed.
538 * flag > 0: Line was executed.
540 * @param boolean $filterTests
543 public function getCodeCoverageInformation($filterTests = TRUE)
545 return PHPUnit_Util_Filter::getFilteredCodeCoverage(
546 $this->codeCoverageInformation, $filterTests
551 * Returns unfiltered Code Coverage data per test case.
552 * Returns data in the same form as getCodeCoverageInformation().
556 public function getUncoveredWhitelistFiles()
558 list(, $missing) = PHPUnit_Util_Filter::getFileCodeCoverageDisposition(
559 $this->codeCoverageInformation
568 * @param PHPUnit_Framework_Test $test
570 public function run(PHPUnit_Framework_Test $test)
572 PHPUnit_Framework_Assert::resetCount();
577 $this->startTest($test);
579 $errorHandlerSet = FALSE;
581 if ($this->convertErrorsToExceptions) {
582 $oldErrorHandler = set_error_handler(
583 array('PHPUnit_Util_ErrorHandler', 'handleError'), E_ALL | E_STRICT
586 if ($oldErrorHandler === NULL) {
587 $errorHandlerSet = TRUE;
589 restore_error_handler();
593 if (self::$xdebugLoaded === NULL) {
594 self::$xdebugLoaded = extension_loaded('xdebug');
595 self::$useXdebug = self::$xdebugLoaded;
598 $useXdebug = self::$useXdebug && $this->collectCodeCoverageInformation && !$test instanceof PHPUnit_Extensions_SeleniumTestCase;
601 xdebug_start_code_coverage(XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE);
604 PHPUnit_Util_Timer::start();
610 catch (PHPUnit_Framework_AssertionFailedError $e) {
614 catch (Exception $e) {
618 $time = PHPUnit_Util_Timer::stop();
621 $codeCoverage = xdebug_get_code_coverage();
622 xdebug_stop_code_coverage();
624 $this->appendCodeCoverageInformation(
629 if ($errorHandlerSet === TRUE) {
630 restore_error_handler();
633 $test->addToAssertionCount(PHPUnit_Framework_Assert::getCount());
635 if ($error === TRUE) {
636 $this->addError($test, $e, $time);
639 else if ($failure === TRUE) {
640 $this->addFailure($test, $e, $time);
643 $this->endTest($test, $time);
647 * Gets the number of run tests.
651 public function count()
653 return $this->runTests;
657 * Checks whether the test run should stop.
661 public function shouldStop()
667 * Marks that the test run should stop.
670 public function stop()
676 * Enables or disables the error-to-exception conversion.
678 * @param boolean $flag
679 * @throws InvalidArgumentException
680 * @since Method available since Release 3.2.14
682 public function convertErrorsToExceptions($flag)
684 if (is_bool($flag)) {
685 $this->convertErrorsToExceptions = $flag;
687 throw new InvalidArgumentException;
692 * Enables or disables the stopping when a failure or error occurs.
694 * @param boolean $flag
695 * @throws InvalidArgumentException
696 * @since Method available since Release 3.1.0
698 public function stopOnFailure($flag)
700 if (is_bool($flag)) {
701 $this->stopOnFailure = $flag;
703 throw new InvalidArgumentException;
708 * Returns the time spent running the tests.
712 public function time()
718 * Returns whether the entire test was successful or not.
722 public function wasSuccessful()
724 return empty($this->errors) && empty($this->failures);