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.3.0
47 require_once 'PHPUnit/Framework.php';
48 require_once 'PHPUnit/Util/Class.php';
49 require_once 'PHPUnit/Util/Filter.php';
50 require_once 'PHPUnit/Util/Printer.php';
51 require_once 'PHPUnit/Util/XML.php';
53 PHPUnit_Util_Filter::addFileToFilter(__FILE__, 'PHPUNIT');
56 * A TestListener that generates a logfile of the test execution in XML markup.
58 * The XML markup used is the same as the one that is used by the JUnit Ant task.
62 * @author Sebastian Bergmann <sb@sebastian-bergmann.de>
63 * @copyright 2002-2009 Sebastian Bergmann <sb@sebastian-bergmann.de>
64 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
65 * @version Release: 3.3.17
66 * @link http://www.phpunit.de/
67 * @since Class available since Release 2.1.0
69 class PHPUnit_Util_Log_XML extends PHPUnit_Util_Printer implements PHPUnit_Framework_TestListener
84 protected $logIncompleteSkipped = FALSE;
89 protected $writeDocument = TRUE;
94 protected $testSuites = array();
99 protected $testSuiteTests = array(0);
104 protected $testSuiteAssertions = array(0);
109 protected $testSuiteErrors = array(0);
114 protected $testSuiteFailures = array(0);
119 protected $testSuiteTimes = array(0);
124 protected $testSuiteLevel = 0;
129 protected $currentTestCase = NULL;
134 protected $attachCurrentTestCase = TRUE;
140 * @param boolean $logIncompleteSkipped
142 public function __construct($out = NULL, $logIncompleteSkipped = FALSE)
144 $this->document = new DOMDocument('1.0', 'UTF-8');
145 $this->document->formatOutput = TRUE;
147 $this->root = $this->document->createElement('testsuites');
148 $this->document->appendChild($this->root);
150 parent::__construct($out);
152 $this->logIncompleteSkipped = $logIncompleteSkipped;
156 * Flush buffer and close output.
159 public function flush()
161 if ($this->writeDocument === TRUE) {
162 $this->write($this->getXML());
171 * @param PHPUnit_Framework_Test $test
172 * @param Exception $e
175 public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
177 if ($test instanceof PHPUnit_Framework_SelfDescribing) {
178 $buffer = $test->toString() . "\n";
183 $buffer .= PHPUnit_Framework_TestFailure::exceptionToString($e) . "\n" .
184 PHPUnit_Util_Filter::getFilteredStacktrace($e, FALSE);
186 $error = $this->document->createElement(
189 PHPUnit_Util_XML::convertToUtf8($buffer),
195 $error->setAttribute('type', get_class($e));
197 $this->currentTestCase->appendChild($error);
199 $this->testSuiteErrors[$this->testSuiteLevel]++;
203 * A failure occurred.
205 * @param PHPUnit_Framework_Test $test
206 * @param PHPUnit_Framework_AssertionFailedError $e
209 public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
211 if (!$test instanceof PHPUnit_Framework_Warning) {
212 if ($test instanceof PHPUnit_Framework_SelfDescribing) {
213 $buffer = $test->toString() . "\n";
218 $buffer .= PHPUnit_Framework_TestFailure::exceptionToString($e) . "\n" .
219 PHPUnit_Util_Filter::getFilteredStacktrace($e, FALSE);
221 $failure = $this->document->createElement(
224 PHPUnit_Util_XML::convertToUtf8($buffer),
230 $failure->setAttribute('type', get_class($e));
232 $this->currentTestCase->appendChild($failure);
234 $this->testSuiteFailures[$this->testSuiteLevel]++;
241 * @param PHPUnit_Framework_Test $test
242 * @param Exception $e
245 public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
247 if ($this->logIncompleteSkipped) {
248 $error = $this->document->createElement(
251 PHPUnit_Util_XML::convertToUtf8(
252 "Incomplete Test\n" .
253 PHPUnit_Util_Filter::getFilteredStacktrace($e, FALSE)
260 $error->setAttribute('type', get_class($e));
262 $this->currentTestCase->appendChild($error);
264 $this->testSuiteErrors[$this->testSuiteLevel]++;
266 $this->attachCurrentTestCase = FALSE;
273 * @param PHPUnit_Framework_Test $test
274 * @param Exception $e
276 * @since Method available since Release 3.0.0
278 public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
280 if ($this->logIncompleteSkipped) {
281 $error = $this->document->createElement(
284 PHPUnit_Util_XML::convertToUtf8(
286 PHPUnit_Util_Filter::getFilteredStacktrace($e, FALSE)
293 $error->setAttribute('type', get_class($e));
295 $this->currentTestCase->appendChild($error);
297 $this->testSuiteErrors[$this->testSuiteLevel]++;
299 $this->attachCurrentTestCase = FALSE;
304 * A testsuite started.
306 * @param PHPUnit_Framework_TestSuite $suite
307 * @since Method available since Release 2.2.0
309 public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
311 $testSuite = $this->document->createElement('testsuite');
312 $testSuite->setAttribute('name', $suite->getName());
314 if (class_exists($suite->getName(), FALSE)) {
316 $class = new ReflectionClass($suite->getName());
318 $testSuite->setAttribute('file', $class->getFileName());
320 $packageInformation = PHPUnit_Util_Class::getPackageInformation(
324 if (!empty($packageInformation['namespace'])) {
325 $testSuite->setAttribute('namespace', $packageInformation['namespace']);
328 if (!empty($packageInformation['fullPackage'])) {
329 $testSuite->setAttribute('fullPackage', $packageInformation['fullPackage']);
332 if (!empty($packageInformation['category'])) {
333 $testSuite->setAttribute('category', $packageInformation['category']);
336 if (!empty($packageInformation['package'])) {
337 $testSuite->setAttribute('package', $packageInformation['package']);
340 if (!empty($packageInformation['subpackage'])) {
341 $testSuite->setAttribute('subpackage', $packageInformation['subpackage']);
345 catch (ReflectionException $e) {
349 if ($this->testSuiteLevel > 0) {
350 $this->testSuites[$this->testSuiteLevel]->appendChild($testSuite);
352 $this->root->appendChild($testSuite);
355 $this->testSuiteLevel++;
356 $this->testSuites[$this->testSuiteLevel] = $testSuite;
357 $this->testSuiteTests[$this->testSuiteLevel] = 0;
358 $this->testSuiteAssertions[$this->testSuiteLevel] = 0;
359 $this->testSuiteErrors[$this->testSuiteLevel] = 0;
360 $this->testSuiteFailures[$this->testSuiteLevel] = 0;
361 $this->testSuiteTimes[$this->testSuiteLevel] = 0;
367 * @param PHPUnit_Framework_TestSuite $suite
368 * @since Method available since Release 2.2.0
370 public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
372 $this->testSuites[$this->testSuiteLevel]->setAttribute('tests', $this->testSuiteTests[$this->testSuiteLevel]);
373 $this->testSuites[$this->testSuiteLevel]->setAttribute('assertions', $this->testSuiteAssertions[$this->testSuiteLevel]);
374 $this->testSuites[$this->testSuiteLevel]->setAttribute('failures', $this->testSuiteFailures[$this->testSuiteLevel]);
375 $this->testSuites[$this->testSuiteLevel]->setAttribute('errors', $this->testSuiteErrors[$this->testSuiteLevel]);
376 $this->testSuites[$this->testSuiteLevel]->setAttribute('time', sprintf('%F', $this->testSuiteTimes[$this->testSuiteLevel]));
378 if ($this->testSuiteLevel > 1) {
379 $this->testSuiteTests[$this->testSuiteLevel - 1] += $this->testSuiteTests[$this->testSuiteLevel];
380 $this->testSuiteAssertions[$this->testSuiteLevel - 1] += $this->testSuiteAssertions[$this->testSuiteLevel];
381 $this->testSuiteErrors[$this->testSuiteLevel - 1] += $this->testSuiteErrors[$this->testSuiteLevel];
382 $this->testSuiteFailures[$this->testSuiteLevel - 1] += $this->testSuiteFailures[$this->testSuiteLevel];
383 $this->testSuiteTimes[$this->testSuiteLevel - 1] += $this->testSuiteTimes[$this->testSuiteLevel];
386 $this->testSuiteLevel--;
392 * @param PHPUnit_Framework_Test $test
394 public function startTest(PHPUnit_Framework_Test $test)
396 if (!$test instanceof PHPUnit_Framework_Warning) {
397 $testCase = $this->document->createElement('testcase');
398 $testCase->setAttribute('name', $test->getName());
400 if ($test instanceof PHPUnit_Framework_TestCase) {
401 $class = new ReflectionClass($test);
402 $methodName = $test->getName();
404 if ($class->hasMethod($methodName)) {
405 $method = $class->getMethod($test->getName());
407 $testCase->setAttribute('class', $class->getName());
408 $testCase->setAttribute('file', $class->getFileName());
409 $testCase->setAttribute('line', $method->getStartLine());
413 $this->currentTestCase = $testCase;
420 * @param PHPUnit_Framework_Test $test
423 public function endTest(PHPUnit_Framework_Test $test, $time)
425 if (!$test instanceof PHPUnit_Framework_Warning) {
426 if ($this->attachCurrentTestCase) {
427 if ($test instanceof PHPUnit_Framework_TestCase) {
428 $numAssertions = $test->getNumAssertions();
429 $this->testSuiteAssertions[$this->testSuiteLevel] += $numAssertions;
431 $this->currentTestCase->setAttribute('assertions', $numAssertions);
434 $this->currentTestCase->setAttribute('time', sprintf('%F', $time));
436 $this->testSuites[$this->testSuiteLevel]->appendChild(
437 $this->currentTestCase
440 $this->testSuiteTests[$this->testSuiteLevel]++;
441 $this->testSuiteTimes[$this->testSuiteLevel] += $time;
445 $this->attachCurrentTestCase = TRUE;
446 $this->currentTestCase = NULL;
450 * Returns the XML as a string.
453 * @since Method available since Release 2.2.0
455 public function getXML()
457 return $this->document->saveXML();
461 * Enables or disables the writing of the document
464 * This is a "hack" needed for the integration of
465 * PHPUnit with Phing.
468 * @since Method available since Release 2.2.0
470 public function setWriteDocument($flag)
472 if (is_bool($flag)) {
473 $this->writeDocument = $flag;