1<?php
2/**
3 *	@package	SimpleTest
4 *	@subpackage	Extensions
5 *	@version	$Id$
6 *  @author Patrice Neff - mailinglists@patrice.ch (original code)
7 */
8
9/**
10 * include SimpleTest reporter
11 */
12require_once dirname(__FILE__).'/../reporter.php';
13
14/**
15 * Reporter which outputs test results in a format compatible
16 * with JUnit / Maven XML output. Can be used for integrating
17 * test suite with continuous integration servers such as
18 * Atlassian Bamboo.
19 * @package	SimpleTest
20 * @subpackage	Extensions
21 */
22class JUnitXMLReporter extends SimpleReporter {
23    function __construct() {
24        parent::__construct();
25        $this->doc = new DOMDocument();
26        $this->doc->loadXML('<testsuite/>');
27        $this->root = $this->doc->documentElement;
28    }
29
30    function paintHeader($test_name) {
31        $this->testsStart = microtime(true);
32
33        $this->root->setAttribute('name', $test_name);
34        $this->root->setAttribute('timestamp', date('c'));
35        $this->root->setAttribute('hostname', 'localhost');
36
37        echo "<?xml version=\"1.0\"?>\n";
38        echo "<!-- starting test suite $test_name\n";
39    }
40
41    /**
42     *    Paints the end of the test with a summary of
43     *    the passes and failures.
44     *    @param string $test_name        Name class of test.
45     *    @access public
46     */
47    function paintFooter($test_name) {
48        echo "-->\n";
49        echo "\n<testsuites>\n";
50
51        $duration = microtime(true) - $this->testsStart;
52
53        $this->root->setAttribute('tests', $this->getPassCount() + $this->getFailCount() + $this->getExceptionCount());
54        $this->root->setAttribute('failures', $this->getFailCount());
55        $this->root->setAttribute('errors', $this->getExceptionCount());
56        $this->root->setAttribute('time', $duration);
57
58        $this->doc->formatOutput = true;
59        $xml = $this->doc->saveXML();
60        // Cut out XML declaration
61        echo preg_replace('/<\?[^>]*\?>/', "", $xml);
62        echo "</testsuites>\n";
63    }
64
65    function paintCaseStart($case) {
66        echo "- case start $case\n";
67        $this->currentCaseName = $case;
68    }
69
70    function paintCaseEnd($case) {
71        // No output here
72    }
73
74    function paintMethodStart($test) {
75        echo "  - test start: $test\n";
76
77        $this->methodStart = microtime(true);
78        $this->currCase = $this->doc->createElement('testcase');
79    }
80
81    function paintMethodEnd($test) {
82        $duration = microtime(true) - $this->methodStart;
83
84        $this->currCase->setAttribute('name', $test);
85        $this->currCase->setAttribute('classname', $this->currentCaseName);
86        $this->currCase->setAttribute('time', $duration);
87        $this->root->appendChild($this->currCase);
88    }
89
90    function paintFail($message) {
91        parent::paintFail($message);
92
93        error_log("Failure: " . $message);
94        $this->terminateAbnormally($message);
95    }
96
97    function paintException($exception) {
98        parent::paintException($exception);
99
100        error_log("Exception: " . $exception);
101        $this->terminateAbnormally($exception);
102    }
103
104    function terminateAbnormally($message) {
105        if (!$this->currCase) {
106            error_log("!! currCase was not set.");
107            return;
108        }
109
110        $ch = $this->doc->createElement('failure');
111        $breadcrumb = $this->getTestList();
112        $ch->setAttribute('message', $breadcrumb[count($breadcrumb)-1]);
113        $ch->setAttribute('type', $breadcrumb[count($breadcrumb)-1]);
114
115        $message = implode(' -> ', $breadcrumb) . "\n\n\n" . $message;
116        $content = $this->doc->createTextNode($message);
117        $ch->appendChild($content);
118
119        $this->currCase->appendChild($ch);
120    }
121}
122?>