3 * base include file for SimpleTest
5 * @subpackage UnitTester
10 * include other SimpleTest class files
12 require_once(dirname(__FILE__
) . '/scorer.php');
16 * Creates the XML needed for remote communication
19 * @subpackage UnitTester
21 class XmlReporter
extends SimpleReporter
{
26 * Sets up indentation and namespace.
27 * @param string $namespace Namespace to add to each tag.
28 * @param string $indent Indenting to add on each nesting.
31 function XmlReporter($namespace = false, $indent = ' ') {
32 $this->SimpleReporter();
33 $this->_namespace
= ($namespace ?
$namespace . ':' : '');
34 $this->_indent
= $indent;
38 * Calculates the pretty printing indent level
39 * from the current level of nesting.
40 * @param integer $offset Extra indenting level.
41 * @return string Leading space.
44 function _getIndent($offset = 0) {
47 count($this->getTestList()) +
$offset);
51 * Converts character string to parsed XML
53 * @param string text Unparsed character data.
54 * @return string Parsed character data.
57 function toParsedXml($text) {
59 array('&', '<', '>', '"', '\''),
60 array('&', '<', '>', '"', '''),
65 * Paints the start of a group test.
66 * @param string $test_name Name of test that is starting.
67 * @param integer $size Number of test cases starting.
70 function paintGroupStart($test_name, $size) {
71 parent
::paintGroupStart($test_name, $size);
72 print $this->_getIndent();
73 print "<" . $this->_namespace
. "group size=\"$size\">\n";
74 print $this->_getIndent(1);
75 print "<" . $this->_namespace
. "name>" .
76 $this->toParsedXml($test_name) .
77 "</" . $this->_namespace
. "name>\n";
81 * Paints the end of a group test.
82 * @param string $test_name Name of test that is ending.
85 function paintGroupEnd($test_name) {
86 print $this->_getIndent();
87 print "</" . $this->_namespace
. "group>\n";
88 parent
::paintGroupEnd($test_name);
92 * Paints the start of a test case.
93 * @param string $test_name Name of test that is starting.
96 function paintCaseStart($test_name) {
97 parent
::paintCaseStart($test_name);
98 print $this->_getIndent();
99 print "<" . $this->_namespace
. "case>\n";
100 print $this->_getIndent(1);
101 print "<" . $this->_namespace
. "name>" .
102 $this->toParsedXml($test_name) .
103 "</" . $this->_namespace
. "name>\n";
107 * Paints the end of a test case.
108 * @param string $test_name Name of test that is ending.
111 function paintCaseEnd($test_name) {
112 print $this->_getIndent();
113 print "</" . $this->_namespace
. "case>\n";
114 parent
::paintCaseEnd($test_name);
118 * Paints the start of a test method.
119 * @param string $test_name Name of test that is starting.
122 function paintMethodStart($test_name) {
123 parent
::paintMethodStart($test_name);
124 print $this->_getIndent();
125 print "<" . $this->_namespace
. "test>\n";
126 print $this->_getIndent(1);
127 print "<" . $this->_namespace
. "name>" .
128 $this->toParsedXml($test_name) .
129 "</" . $this->_namespace
. "name>\n";
133 * Paints the end of a test method.
134 * @param string $test_name Name of test that is ending.
135 * @param integer $progress Number of test cases ending.
138 function paintMethodEnd($test_name) {
139 print $this->_getIndent();
140 print "</" . $this->_namespace
. "test>\n";
141 parent
::paintMethodEnd($test_name);
145 * Paints pass as XML.
146 * @param string $message Message to encode.
149 function paintPass($message) {
150 parent
::paintPass($message);
151 print $this->_getIndent(1);
152 print "<" . $this->_namespace
. "pass>";
153 print $this->toParsedXml($message);
154 print "</" . $this->_namespace
. "pass>\n";
158 * Paints failure as XML.
159 * @param string $message Message to encode.
162 function paintFail($message) {
163 parent
::paintFail($message);
164 print $this->_getIndent(1);
165 print "<" . $this->_namespace
. "fail>";
166 print $this->toParsedXml($message);
167 print "</" . $this->_namespace
. "fail>\n";
171 * Paints error as XML.
172 * @param string $message Message to encode.
175 function paintError($message) {
176 parent
::paintError($message);
177 print $this->_getIndent(1);
178 print "<" . $this->_namespace
. "exception>";
179 print $this->toParsedXml($message);
180 print "</" . $this->_namespace
. "exception>\n";
184 * Paints exception as XML.
185 * @param Exception $exception Exception to encode.
188 function paintException($exception) {
189 parent
::paintException($exception);
190 print $this->_getIndent(1);
191 print "<" . $this->_namespace
. "exception>";
192 $message = 'Unexpected exception of type [' . get_class($exception) .
193 '] with message ['. $exception->getMessage() .
194 '] in ['. $exception->getFile() .
195 ' line ' . $exception->getLine() . ']';
196 print $this->toParsedXml($message);
197 print "</" . $this->_namespace
. "exception>\n";
201 * Paints the skipping message and tag.
202 * @param string $message Text to display in skip tag.
205 function paintSkip($message) {
206 parent
::paintSkip($message);
207 print $this->_getIndent(1);
208 print "<" . $this->_namespace
. "skip>";
209 print $this->toParsedXml($message);
210 print "</" . $this->_namespace
. "skip>\n";
214 * Paints a simple supplementary message.
215 * @param string $message Text to display.
218 function paintMessage($message) {
219 parent
::paintMessage($message);
220 print $this->_getIndent(1);
221 print "<" . $this->_namespace
. "message>";
222 print $this->toParsedXml($message);
223 print "</" . $this->_namespace
. "message>\n";
227 * Paints a formatted ASCII message such as a
229 * @param string $message Text to display.
232 function paintFormattedMessage($message) {
233 parent
::paintFormattedMessage($message);
234 print $this->_getIndent(1);
235 print "<" . $this->_namespace
. "formatted>";
236 print "<![CDATA[$message]]>";
237 print "</" . $this->_namespace
. "formatted>\n";
241 * Serialises the event object.
242 * @param string $type Event type as text.
243 * @param mixed $payload Message or object.
246 function paintSignal($type, &$payload) {
247 parent
::paintSignal($type, $payload);
248 print $this->_getIndent(1);
249 print "<" . $this->_namespace
. "signal type=\"$type\">";
250 print "<![CDATA[" . serialize($payload) . "]]>";
251 print "</" . $this->_namespace
. "signal>\n";
255 * Paints the test document header.
256 * @param string $test_name First test top level
261 function paintHeader($test_name) {
262 if (! SimpleReporter
::inCli()) {
263 header('Content-type: text/xml');
265 print "<?xml version=\"1.0\"";
266 if ($this->_namespace
) {
267 print " xmlns:" . $this->_namespace
.
268 "=\"www.lastcraft.com/SimpleTest/Beta3/Report\"";
271 print "<" . $this->_namespace
. "run>\n";
275 * Paints the test document footer.
276 * @param string $test_name The top level test.
280 function paintFooter($test_name) {
281 print "</" . $this->_namespace
. "run>\n";
286 * Accumulator for incoming tag. Holds the
287 * incoming test structure information for
288 * later dispatch to the reporter.
289 * @package SimpleTest
290 * @subpackage UnitTester
292 class NestingXmlTag
{
297 * Sets the basic test information except
299 * @param hash $attributes Name value pairs.
302 function NestingXmlTag($attributes) {
303 $this->_name
= false;
304 $this->_attributes
= $attributes;
308 * Sets the test case/method name.
309 * @param string $name Name of test.
312 function setName($name) {
313 $this->_name
= $name;
318 * @return string Name of test.
326 * Accessor for attributes.
327 * @return hash All attributes.
330 function _getAttributes() {
331 return $this->_attributes
;
336 * Accumulator for incoming method tag. Holds the
337 * incoming test structure information for
338 * later dispatch to the reporter.
339 * @package SimpleTest
340 * @subpackage UnitTester
342 class NestingMethodTag
extends NestingXmlTag
{
345 * Sets the basic test information except
347 * @param hash $attributes Name value pairs.
350 function NestingMethodTag($attributes) {
351 $this->NestingXmlTag($attributes);
355 * Signals the appropriate start event on the
357 * @param SimpleReporter $listener Target for events.
360 function paintStart(&$listener) {
361 $listener->paintMethodStart($this->getName());
365 * Signals the appropriate end event on the
367 * @param SimpleReporter $listener Target for events.
370 function paintEnd(&$listener) {
371 $listener->paintMethodEnd($this->getName());
376 * Accumulator for incoming case tag. Holds the
377 * incoming test structure information for
378 * later dispatch to the reporter.
379 * @package SimpleTest
380 * @subpackage UnitTester
382 class NestingCaseTag
extends NestingXmlTag
{
385 * Sets the basic test information except
387 * @param hash $attributes Name value pairs.
390 function NestingCaseTag($attributes) {
391 $this->NestingXmlTag($attributes);
395 * Signals the appropriate start event on the
397 * @param SimpleReporter $listener Target for events.
400 function paintStart(&$listener) {
401 $listener->paintCaseStart($this->getName());
405 * Signals the appropriate end event on the
407 * @param SimpleReporter $listener Target for events.
410 function paintEnd(&$listener) {
411 $listener->paintCaseEnd($this->getName());
416 * Accumulator for incoming group tag. Holds the
417 * incoming test structure information for
418 * later dispatch to the reporter.
419 * @package SimpleTest
420 * @subpackage UnitTester
422 class NestingGroupTag
extends NestingXmlTag
{
425 * Sets the basic test information except
427 * @param hash $attributes Name value pairs.
430 function NestingGroupTag($attributes) {
431 $this->NestingXmlTag($attributes);
435 * Signals the appropriate start event on the
437 * @param SimpleReporter $listener Target for events.
440 function paintStart(&$listener) {
441 $listener->paintGroupStart($this->getName(), $this->getSize());
445 * Signals the appropriate end event on the
447 * @param SimpleReporter $listener Target for events.
450 function paintEnd(&$listener) {
451 $listener->paintGroupEnd($this->getName());
455 * The size in the attributes.
456 * @return integer Value of size attribute or zero.
460 $attributes = $this->_getAttributes();
461 if (isset($attributes['SIZE'])) {
462 return (integer)$attributes['SIZE'];
469 * Parser for importing the output of the XmlReporter.
470 * Dispatches that output to another reporter.
471 * @package SimpleTest
472 * @subpackage UnitTester
474 class SimpleTestXmlParser
{
478 var $_in_content_tag;
483 * Loads a listener with the SimpleReporter
485 * @param SimpleReporter $listener Listener of tag events.
488 function SimpleTestXmlParser(&$listener) {
489 $this->_listener
= &$listener;
490 $this->_expat
= &$this->_createParser();
491 $this->_tag_stack
= array();
492 $this->_in_content_tag
= false;
493 $this->_content
= '';
494 $this->_attributes
= array();
498 * Parses a block of XML sending the results to
500 * @param string $chunk Block of text to read.
501 * @return boolean True if valid XML.
504 function parse($chunk) {
505 if (! xml_parse($this->_expat
, $chunk)) {
506 trigger_error('XML parse error with ' .
507 xml_error_string(xml_get_error_code($this->_expat
)));
514 * Sets up expat as the XML parser.
515 * @return resource Expat handle.
518 function &_createParser() {
519 $expat = xml_parser_create();
520 xml_set_object($expat, $this);
521 xml_set_element_handler($expat, '_startElement', '_endElement');
522 xml_set_character_data_handler($expat, '_addContent');
523 xml_set_default_handler($expat, '_default');
528 * Opens a new test nesting level.
529 * @return NestedXmlTag The group, case or method tag
533 function _pushNestingTag($nested) {
534 array_unshift($this->_tag_stack
, $nested);
538 * Accessor for current test structure tag.
539 * @return NestedXmlTag The group, case or method tag
543 function &_getCurrentNestingTag() {
544 return $this->_tag_stack
[0];
548 * Ends a nesting tag.
549 * @return NestedXmlTag The group, case or method tag
553 function _popNestingTag() {
554 return array_shift($this->_tag_stack
);
558 * Test if tag is a leaf node with only text content.
559 * @param string $tag XML tag name.
560 * @return @boolean True if leaf, false if nesting.
563 function _isLeaf($tag) {
564 return in_array($tag, array(
565 'NAME', 'PASS', 'FAIL', 'EXCEPTION', 'SKIP', 'MESSAGE', 'FORMATTED', 'SIGNAL'));
569 * Handler for start of event element.
570 * @param resource $expat Parser handle.
571 * @param string $tag Element name.
572 * @param hash $attributes Name value pairs.
573 * Attributes without content
574 * are marked as true.
577 function _startElement($expat, $tag, $attributes) {
578 $this->_attributes
= $attributes;
579 if ($tag == 'GROUP') {
580 $this->_pushNestingTag(new NestingGroupTag($attributes));
581 } elseif ($tag == 'CASE') {
582 $this->_pushNestingTag(new NestingCaseTag($attributes));
583 } elseif ($tag == 'TEST') {
584 $this->_pushNestingTag(new NestingMethodTag($attributes));
585 } elseif ($this->_isLeaf($tag)) {
586 $this->_in_content_tag
= true;
587 $this->_content
= '';
592 * End of element event.
593 * @param resource $expat Parser handle.
594 * @param string $tag Element name.
597 function _endElement($expat, $tag) {
598 $this->_in_content_tag
= false;
599 if (in_array($tag, array('GROUP', 'CASE', 'TEST'))) {
600 $nesting_tag = $this->_popNestingTag();
601 $nesting_tag->paintEnd($this->_listener
);
602 } elseif ($tag == 'NAME') {
603 $nesting_tag = &$this->_getCurrentNestingTag();
604 $nesting_tag->setName($this->_content
);
605 $nesting_tag->paintStart($this->_listener
);
606 } elseif ($tag == 'PASS') {
607 $this->_listener
->paintPass($this->_content
);
608 } elseif ($tag == 'FAIL') {
609 $this->_listener
->paintFail($this->_content
);
610 } elseif ($tag == 'EXCEPTION') {
611 $this->_listener
->paintError($this->_content
);
612 } elseif ($tag == 'SKIP') {
613 $this->_listener
->paintSkip($this->_content
);
614 } elseif ($tag == 'SIGNAL') {
615 $this->_listener
->paintSignal(
616 $this->_attributes
['TYPE'],
617 unserialize($this->_content
));
618 } elseif ($tag == 'MESSAGE') {
619 $this->_listener
->paintMessage($this->_content
);
620 } elseif ($tag == 'FORMATTED') {
621 $this->_listener
->paintFormattedMessage($this->_content
);
626 * Content between start and end elements.
627 * @param resource $expat Parser handle.
628 * @param string $text Usually output messages.
631 function _addContent($expat, $text) {
632 if ($this->_in_content_tag
) {
633 $this->_content
.= $text;
639 * XML and Doctype handler. Discards all such content.
640 * @param resource $expat Parser handle.
641 * @param string $default Text of default content.
644 function _default($expat, $default) {