From 4e4a08011c4d875da7f4a312f7ee4879ef86f623 Mon Sep 17 00:00:00 2001 From: sun Date: Fri, 25 Jul 2014 03:36:49 +0200 Subject: [PATCH] Fix #1351: Isolated TestResult contains serialized test class upon failure. --- src/Framework/FlattenedException.php | 183 +++++++++++++++++++++++++++ src/Framework/TestFailure.php | 57 ++++----- src/Framework/TestResult.php | 26 +++- src/TextUI/ResultPrinter.php | 33 +---- src/Util/PHP.php | 3 +- 5 files changed, 238 insertions(+), 64 deletions(-) create mode 100644 src/Framework/FlattenedException.php diff --git a/src/Framework/FlattenedException.php b/src/Framework/FlattenedException.php new file mode 100644 index 00000000000..25991c97685 --- /dev/null +++ b/src/Framework/FlattenedException.php @@ -0,0 +1,183 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package PHPUnit + * @subpackage Framework + * @author Sebastian Bergmann + * @copyright 2001-2014 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since File available since Release 3.0.0 + */ + +/** + * String representation of an Exception. + * + * @see PHPUnit_Framework_TestFailure + * + * @package PHPUnit + * @subpackage Framework + * @author Daniel F. Kudwien + * @copyright 2001-2014 Sebastian Bergmann + * @license http://www.opensource.org/licenses/BSD-3-Clause The BSD 3-Clause License + * @link http://www.phpunit.de/ + * @since Class available since Release 4.1.5 + */ +class PHPUnit_Framework_FlattenedException +{ + /** + * @var string + */ + protected $message; + + /** + * @var PHPUnit_Framework_FlattenedException|null + */ + protected $previous; + + /** + * @var string + */ + protected $asString; + + /** + * @var string + */ + protected $trace; + + /** + * @var boolean + */ + protected $isFailure; + + /** + * Constructs a FlattenedException. + * + * @param Exception $e + */ + public function __construct(Exception $e) + { + $this->message = $e->getMessage(); + if ($e->getPrevious()) { + $this->previous = new self($e->getPrevious()); + } + $this->asString = self::toString($e); + $this->trace = PHPUnit_Util_Filter::getFilteredStacktrace($e); + $this->isFailure = $e instanceof PHPUnit_Framework_AssertionFailedError; + } + + /** + * @return string + */ + public function getMessage() + { + return $this->message; + } + + /** + * @return PHPUnit_Framework_FlattenedException + */ + public function getPrevious() + { + return $this->previous; + } + + /** + * @return string + */ + public function getTraceAsString() + { + return $this->trace; + } + + /** + * @return string + */ + public function asString() + { + return $this->asString; + } + + /** + * @return string + */ + public function __toString() + { + $string = $this->asString; + if (!empty($this->trace)) { + $string .= "\n" . $this->trace; + } + return $string; + } + + /** + * Returns a description for an exception. + * + * @param Exception $e + * @return string + * @since Method available since Release 4.1.5 + */ + public static function toString(Exception $e) + { + if ($e instanceof PHPUnit_Framework_SelfDescribing) { + $buffer = $e->toString(); + + if ($e instanceof PHPUnit_Framework_ExpectationFailedException && $e->getComparisonFailure()) { + $buffer = $buffer . $e->getComparisonFailure()->getDiff(); + } + + if (!empty($buffer)) { + $buffer = trim($buffer) . "\n"; + } + } elseif ($e instanceof PHPUnit_Framework_Error) { + $buffer = $e->getMessage() . "\n"; + } else { + $buffer = get_class($e) . ': ' . $e->getMessage() . "\n"; + } + + return $buffer; + } + + /** + * Returns whether a PHPUnit_Framework_AssertionFailedError was thrown. + * + * @return boolean + */ + public function isFailure() + { + return $this->isFailure; + } +} diff --git a/src/Framework/TestFailure.php b/src/Framework/TestFailure.php index 9da0b739efe..ccbe34ef0ff 100644 --- a/src/Framework/TestFailure.php +++ b/src/Framework/TestFailure.php @@ -57,12 +57,12 @@ class PHPUnit_Framework_TestFailure { /** - * @var PHPUnit_Framework_Test + * @var string */ - protected $failedTest; + private $label; /** - * @var Exception + * @var PHPUnit_Framework_FlattenedException */ protected $thrownException; @@ -70,12 +70,18 @@ class PHPUnit_Framework_TestFailure * Constructs a TestFailure with the given test and exception. * * @param PHPUnit_Framework_Test $failedTest - * @param Exception $thrownException + * @param Exception|PHPUnit_Framework_FlattenedException $thrownException */ - public function __construct(PHPUnit_Framework_Test $failedTest, Exception $thrownException) + public function __construct(PHPUnit_Framework_Test $failedTest, $thrownException) { - $this->failedTest = $failedTest; - $this->thrownException = $thrownException; + $this->label = $failedTest->toString(); + + if ($thrownException instanceof PHPUnit_Framework_FlattenedException) { + $this->thrownException = $thrownException; + } + else { + $this->thrownException = new PHPUnit_Framework_FlattenedException($thrownException); + } } /** @@ -87,8 +93,7 @@ public function toString() { return sprintf( '%s: %s', - - $this->failedTest->toString(), + $this->label, $this->thrownException->getMessage() ); } @@ -101,7 +106,7 @@ public function toString() */ public function getExceptionAsString() { - return self::exceptionToString($this->thrownException); + return $this->thrownException->asString(); } /** @@ -110,42 +115,28 @@ public function getExceptionAsString() * @param Exception $e * @return string * @since Method available since Release 3.2.0 + * @deprecated 4.1.5 + * @see PHPUnit_Framework_FlattenedException */ public static function exceptionToString(Exception $e) { - if ($e instanceof PHPUnit_Framework_SelfDescribing) { - $buffer = $e->toString(); - - if ($e instanceof PHPUnit_Framework_ExpectationFailedException && $e->getComparisonFailure()) { - $buffer = $buffer . $e->getComparisonFailure()->getDiff(); - } - - if (!empty($buffer)) { - $buffer = trim($buffer) . "\n"; - } - } elseif ($e instanceof PHPUnit_Framework_Error) { - $buffer = $e->getMessage() . "\n"; - } else { - $buffer = get_class($e) . ': ' . $e->getMessage() . "\n"; - } - - return $buffer; + return PHPUnit_Framework_FlattenedException::toString($e); } /** - * Gets the failed test. + * Returns the label of the failing test (including data set, if any). * - * @return PHPUnit_Framework_Test + * @return string */ - public function failedTest() + public function getTestLabel() { - return $this->failedTest; + return $this->label; } /** * Gets the thrown exception. * - * @return Exception + * @return PHPUnit_Framework_FlattenedException */ public function thrownException() { @@ -170,6 +161,6 @@ public function exceptionMessage() */ public function isFailure() { - return ($this->thrownException() instanceof PHPUnit_Framework_AssertionFailedError); + return $this->thrownException->isFailure(); } } diff --git a/src/Framework/TestResult.php b/src/Framework/TestResult.php index f373b082f83..c8fb2946113 100644 --- a/src/Framework/TestResult.php +++ b/src/Framework/TestResult.php @@ -284,10 +284,10 @@ public function addError(PHPUnit_Framework_Test $test, Exception $e, $time) * The passed in exception caused the failure. * * @param PHPUnit_Framework_Test $test - * @param PHPUnit_Framework_AssertionFailedError $e + * @param PHPUnit_Framework_AssertionFailedError|PHPUnit_Framework_FlattenedException $e * @param float $time */ - public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) + public function addFailure(PHPUnit_Framework_Test $test, $e, $time) { if ($e instanceof PHPUnit_Framework_RiskyTest) { $this->risky[] = new PHPUnit_Framework_TestFailure( @@ -316,6 +316,13 @@ public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_Asser if ($this->stopOnSkipped) { $this->stop(); } +// } elseif ($e instanceof PHPUnit_Framework_TestFailure) { +// $this->failures[] = $e; +// $notifyMethod = 'addFailure'; +// +// if ($this->stopOnFailure) { +// $this->stop(); +// } } else { $this->failures[] = new PHPUnit_Framework_TestFailure($test, $e); $notifyMethod = 'addFailure'; @@ -325,6 +332,21 @@ public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_Asser } } + // @todo Excessive test result processing recursion: + // PHPUnit_Util_PHP::processChildResult() === TestFailure + // -> PHPUnit_Util_PHP::getException() + // -> FlattenedException + // self::addFailure(, FlattenedException) + // ^^ -> new TestFailure + // -> FlattenedException + // PHPUnit_Framework_FlattenedException <---| + // + // 1. -> TestResult::add(TestResult $result) + // 2. PHPUnit_Util_Printer expects AssertionFailedError... :-| + if ($e instanceof PHPUnit_Framework_FlattenedException) { + $e = new PHPUnit_Framework_AssertionFailedError($e->getMessage()); + } + foreach ($this->listeners as $listener) { $listener->$notifyMethod($test, $e, $time); } diff --git a/src/TextUI/ResultPrinter.php b/src/TextUI/ResultPrinter.php index 3a316c19bed..572f51507a0 100644 --- a/src/TextUI/ResultPrinter.php +++ b/src/TextUI/ResultPrinter.php @@ -260,20 +260,12 @@ protected function printDefect(PHPUnit_Framework_TestFailure $defect, $count) */ protected function printDefectHeader(PHPUnit_Framework_TestFailure $defect, $count) { - $failedTest = $defect->failedTest(); - - if ($failedTest instanceof PHPUnit_Framework_SelfDescribing) { - $testName = $failedTest->toString(); - } else { - $testName = get_class($failedTest); - } - $this->write( sprintf( "\n%d) %s\n", $count, - $testName + $defect->getTestLabel() ) ); } @@ -283,26 +275,11 @@ protected function printDefectHeader(PHPUnit_Framework_TestFailure $defect, $cou */ protected function printDefectTrace(PHPUnit_Framework_TestFailure $defect) { - $this->write($defect->getExceptionAsString()); - - $trace = PHPUnit_Util_Filter::getFilteredStacktrace( - $defect->thrownException() - ); - - if (!empty($trace)) { - $this->write("\n" . $trace); - } - - $e = $defect->thrownException()->getPrevious(); - - while ($e) { - $this->write( - "\nCaused by\n" . - PHPUnit_Framework_TestFailure::exceptionToString($e). "\n" . - PHPUnit_Util_Filter::getFilteredStacktrace($e) - ); + $e = $defect->thrownException(); + $this->write((string) $e); - $e = $e->getPrevious(); + while ($e = $e->getPrevious()) { + $this->write("\nCaused by\n" . $e); } } diff --git a/src/Util/PHP.php b/src/Util/PHP.php index d3d36a06f51..9fa08717c11 100644 --- a/src/Util/PHP.php +++ b/src/Util/PHP.php @@ -193,7 +193,7 @@ private function processChildResult(PHPUnit_Framework_Test $test, PHPUnit_Framew ); } elseif (!empty($failures)) { $result->addFailure( - $test, $this->getException($failures[0]), $time + $test, $failures[0]->thrownException(), $time ); } } @@ -218,6 +218,7 @@ private function getException(PHPUnit_Framework_TestFailure $error) { $exception = $error->thrownException(); + // @todo Obsolete? if ($exception instanceof __PHP_Incomplete_Class) { $exceptionArray = array(); foreach ((array) $exception as $key => $value) {