3 * base include file for SimpleTest
5 * @subpackage UnitTester
6 * @version $Id: xml.php,v 1.22 2006/02/06 06:05:18 lastcraft Exp $
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
{
29 function XmlReporter($namespace = false, $indent = ' ') {
30 $this->SimpleReporter();
31 $this->_namespace
= ($namespace ?
$namespace . ':' : '');
32 $this->_indent
= $indent;
36 * Calculates the pretty printing indent level
37 * from the current level of nesting.
38 * @param integer $offset Extra indenting level.
39 * @return string Leading space.
42 function _getIndent($offset = 0) {
45 count($this->getTestList()) +
$offset);
49 * Converts character string to parsed XML
51 * @param string text Unparsed character data.
52 * @return string Parsed character data.
55 function toParsedXml($text) {
57 array('&', '<', '>', '"', '\''),
58 array('&', '<', '>', '"', '''),
63 * Paints the start of a group test.
64 * @param string $test_name Name of test that is starting.
65 * @param integer $size Number of test cases starting.
68 function paintGroupStart($test_name, $size) {
69 parent
::paintGroupStart($test_name, $size);
70 print $this->_getIndent();
71 print "<" . $this->_namespace
. "group size=\"$size\">\n";
72 print $this->_getIndent(1);
73 print "<" . $this->_namespace
. "name>" .
74 $this->toParsedXml($test_name) .
75 "</" . $this->_namespace
. "name>\n";
79 * Paints the end of a group test.
80 * @param string $test_name Name of test that is ending.
83 function paintGroupEnd($test_name) {
84 print $this->_getIndent();
85 print "</" . $this->_namespace
. "group>\n";
86 parent
::paintGroupEnd($test_name);
90 * Paints the start of a test case.
91 * @param string $test_name Name of test that is starting.
94 function paintCaseStart($test_name) {
95 parent
::paintCaseStart($test_name);
96 print $this->_getIndent();
97 print "<" . $this->_namespace
. "case>\n";
98 print $this->_getIndent(1);
99 print "<" . $this->_namespace
. "name>" .
100 $this->toParsedXml($test_name) .
101 "</" . $this->_namespace
. "name>\n";
105 * Paints the end of a test case.
106 * @param string $test_name Name of test that is ending.
109 function paintCaseEnd($test_name) {
110 print $this->_getIndent();
111 print "</" . $this->_namespace
. "case>\n";
112 parent
::paintCaseEnd($test_name);
116 * Paints the start of a test method.
117 * @param string $test_name Name of test that is starting.
120 function paintMethodStart($test_name) {
121 parent
::paintMethodStart($test_name);
122 print $this->_getIndent();
123 print "<" . $this->_namespace
. "test>\n";
124 print $this->_getIndent(1);
125 print "<" . $this->_namespace
. "name>" .
126 $this->toParsedXml($test_name) .
127 "</" . $this->_namespace
. "name>\n";
131 * Paints the end of a test method.
132 * @param string $test_name Name of test that is ending.
133 * @param integer $progress Number of test cases ending.
136 function paintMethodEnd($test_name) {
137 print $this->_getIndent();
138 print "</" . $this->_namespace
. "test>\n";
139 parent
::paintMethodEnd($test_name);
143 * Increments the pass count.
144 * @param string $message Message is ignored.
147 function paintPass($message) {
148 parent
::paintPass($message);
149 print $this->_getIndent(1);
150 print "<" . $this->_namespace
. "pass>";
151 print $this->toParsedXml($message);
152 print "</" . $this->_namespace
. "pass>\n";
156 * Increments the fail count.
157 * @param string $message Message is ignored.
160 function paintFail($message) {
161 parent
::paintFail($message);
162 print $this->_getIndent(1);
163 print "<" . $this->_namespace
. "fail>";
164 print $this->toParsedXml($message);
165 print "</" . $this->_namespace
. "fail>\n";
169 * Paints a PHP error or exception.
170 * @param string $message Message is ignored.
174 function paintError($message) {
175 parent
::paintError($message);
176 print $this->_getIndent(1);
177 print "<" . $this->_namespace
. "exception>";
178 print $this->toParsedXml($message);
179 print "</" . $this->_namespace
. "exception>\n";
183 * Paints a simple supplementary message.
184 * @param string $message Text to display.
187 function paintMessage($message) {
188 parent
::paintMessage($message);
189 print $this->_getIndent(1);
190 print "<" . $this->_namespace
. "message>";
191 print $this->toParsedXml($message);
192 print "</" . $this->_namespace
. "message>\n";
196 * Paints a formatted ASCII message such as a
198 * @param string $message Text to display.
201 function paintFormattedMessage($message) {
202 parent
::paintFormattedMessage($message);
203 print $this->_getIndent(1);
204 print "<" . $this->_namespace
. "formatted>";
205 print "<![CDATA[$message]]>";
206 print "</" . $this->_namespace
. "formatted>\n";
210 * Serialises the event object.
211 * @param string $type Event type as text.
212 * @param mixed $payload Message or object.
215 function paintSignal($type, &$payload) {
216 parent
::paintSignal($type, $payload);
217 print $this->_getIndent(1);
218 print "<" . $this->_namespace
. "signal type=\"$type\">";
219 print "<![CDATA[" . serialize($payload) . "]]>";
220 print "</" . $this->_namespace
. "signal>\n";
224 * Paints the test document header.
225 * @param string $test_name First test top level
230 function paintHeader($test_name) {
231 if (! SimpleReporter
::inCli()) {
232 header('Content-type: text/xml');
234 print "<?xml version=\"1.0\"";
235 if ($this->_namespace
) {
236 print " xmlns:" . $this->_namespace
.
237 "=\"www.lastcraft.com/SimpleTest/Beta3/Report\"";
240 print "<" . $this->_namespace
. "run>\n";
244 * Paints the test document footer.
245 * @param string $test_name The top level test.
249 function paintFooter($test_name) {
250 print "</" . $this->_namespace
. "run>\n";
255 * Accumulator for incoming tag. Holds the
256 * incoming test structure information for
257 * later dispatch to the reporter.
258 * @package SimpleTest
259 * @subpackage UnitTester
261 class NestingXmlTag
{
266 * Sets the basic test information except
268 * @param hash $attributes Name value pairs.
271 function NestingXmlTag($attributes) {
272 $this->_name
= false;
273 $this->_attributes
= $attributes;
277 * Sets the test case/method name.
278 * @param string $name Name of test.
281 function setName($name) {
282 $this->_name
= $name;
287 * @return string Name of test.
295 * Accessor for attributes.
296 * @return hash All attributes.
299 function _getAttributes() {
300 return $this->_attributes
;
305 * Accumulator for incoming method tag. Holds the
306 * incoming test structure information for
307 * later dispatch to the reporter.
308 * @package SimpleTest
309 * @subpackage UnitTester
311 class NestingMethodTag
extends NestingXmlTag
{
314 * Sets the basic test information except
316 * @param hash $attributes Name value pairs.
319 function NestingMethodTag($attributes) {
320 $this->NestingXmlTag($attributes);
324 * Signals the appropriate start event on the
326 * @param SimpleReporter $listener Target for events.
329 function paintStart(&$listener) {
330 $listener->paintMethodStart($this->getName());
334 * Signals the appropriate end event on the
336 * @param SimpleReporter $listener Target for events.
339 function paintEnd(&$listener) {
340 $listener->paintMethodEnd($this->getName());
345 * Accumulator for incoming case tag. Holds the
346 * incoming test structure information for
347 * later dispatch to the reporter.
348 * @package SimpleTest
349 * @subpackage UnitTester
351 class NestingCaseTag
extends NestingXmlTag
{
354 * Sets the basic test information except
356 * @param hash $attributes Name value pairs.
359 function NestingCaseTag($attributes) {
360 $this->NestingXmlTag($attributes);
364 * Signals the appropriate start event on the
366 * @param SimpleReporter $listener Target for events.
369 function paintStart(&$listener) {
370 $listener->paintCaseStart($this->getName());
374 * Signals the appropriate end event on the
376 * @param SimpleReporter $listener Target for events.
379 function paintEnd(&$listener) {
380 $listener->paintCaseEnd($this->getName());
385 * Accumulator for incoming group tag. Holds the
386 * incoming test structure information for
387 * later dispatch to the reporter.
388 * @package SimpleTest
389 * @subpackage UnitTester
391 class NestingGroupTag
extends NestingXmlTag
{
394 * Sets the basic test information except
396 * @param hash $attributes Name value pairs.
399 function NestingGroupTag($attributes) {
400 $this->NestingXmlTag($attributes);
404 * Signals the appropriate start event on the
406 * @param SimpleReporter $listener Target for events.
409 function paintStart(&$listener) {
410 $listener->paintGroupStart($this->getName(), $this->getSize());
414 * Signals the appropriate end event on the
416 * @param SimpleReporter $listener Target for events.
419 function paintEnd(&$listener) {
420 $listener->paintGroupEnd($this->getName());
424 * The size in the attributes.
425 * @return integer Value of size attribute or zero.
429 $attributes = $this->_getAttributes();
430 if (isset($attributes['SIZE'])) {
431 return (integer)$attributes['SIZE'];
438 * Parser for importing the output of the XmlReporter.
439 * Dispatches that output to another reporter.
440 * @package SimpleTest
441 * @subpackage UnitTester
443 class SimpleTestXmlParser
{
447 var $_in_content_tag;
452 * Loads a listener with the SimpleReporter
454 * @param SimpleReporter $listener Listener of tag events.
457 function SimpleTestXmlParser(&$listener) {
458 $this->_listener
= &$listener;
459 $this->_expat
= &$this->_createParser();
460 $this->_tag_stack
= array();
461 $this->_in_content_tag
= false;
462 $this->_content
= '';
463 $this->_attributes
= array();
467 * Parses a block of XML sending the results to
469 * @param string $chunk Block of text to read.
470 * @return boolean True if valid XML.
473 function parse($chunk) {
474 if (! xml_parse($this->_expat
, $chunk)) {
475 trigger_error('XML parse error with ' .
476 xml_error_string(xml_get_error_code($this->_expat
)));
483 * Sets up expat as the XML parser.
484 * @return resource Expat handle.
487 function &_createParser() {
488 $expat = xml_parser_create();
489 xml_set_object($expat, $this);
490 xml_set_element_handler($expat, '_startElement', '_endElement');
491 xml_set_character_data_handler($expat, '_addContent');
492 xml_set_default_handler($expat, '_default');
497 * Opens a new test nesting level.
498 * @return NestedXmlTag The group, case or method tag
502 function _pushNestingTag($nested) {
503 array_unshift($this->_tag_stack
, $nested);
507 * Accessor for current test structure tag.
508 * @return NestedXmlTag The group, case or method tag
512 function &_getCurrentNestingTag() {
513 return $this->_tag_stack
[0];
517 * Ends a nesting tag.
518 * @return NestedXmlTag The group, case or method tag
522 function _popNestingTag() {
523 return array_shift($this->_tag_stack
);
527 * Test if tag is a leaf node with only text content.
528 * @param string $tag XML tag name.
529 * @return @boolean True if leaf, false if nesting.
532 function _isLeaf($tag) {
533 return in_array($tag, array(
534 'NAME', 'PASS', 'FAIL', 'EXCEPTION', 'MESSAGE', 'FORMATTED', 'SIGNAL'));
538 * Handler for start of event element.
539 * @param resource $expat Parser handle.
540 * @param string $tag Element name.
541 * @param hash $attributes Name value pairs.
542 * Attributes without content
543 * are marked as true.
546 function _startElement($expat, $tag, $attributes) {
547 $this->_attributes
= $attributes;
548 if ($tag == 'GROUP') {
549 $this->_pushNestingTag(new NestingGroupTag($attributes));
550 } elseif ($tag == 'CASE') {
551 $this->_pushNestingTag(new NestingCaseTag($attributes));
552 } elseif ($tag == 'TEST') {
553 $this->_pushNestingTag(new NestingMethodTag($attributes));
554 } elseif ($this->_isLeaf($tag)) {
555 $this->_in_content_tag
= true;
556 $this->_content
= '';
561 * End of element event.
562 * @param resource $expat Parser handle.
563 * @param string $tag Element name.
566 function _endElement($expat, $tag) {
567 $this->_in_content_tag
= false;
568 if (in_array($tag, array('GROUP', 'CASE', 'TEST'))) {
569 $nesting_tag = $this->_popNestingTag();
570 $nesting_tag->paintEnd($this->_listener
);
571 } elseif ($tag == 'NAME') {
572 $nesting_tag = &$this->_getCurrentNestingTag();
573 $nesting_tag->setName($this->_content
);
574 $nesting_tag->paintStart($this->_listener
);
575 } elseif ($tag == 'PASS') {
576 $this->_listener
->paintPass($this->_content
);
577 } elseif ($tag == 'FAIL') {
578 $this->_listener
->paintFail($this->_content
);
579 } elseif ($tag == 'EXCEPTION') {
580 $this->_listener
->paintError($this->_content
);
581 } elseif ($tag == 'SIGNAL') {
582 $this->_listener
->paintSignal(
583 $this->_attributes
['TYPE'],
584 unserialize($this->_content
));
585 } elseif ($tag == 'MESSAGE') {
586 $this->_listener
->paintMessage($this->_content
);
587 } elseif ($tag == 'FORMATTED') {
588 $this->_listener
->paintFormattedMessage($this->_content
);
593 * Content between start and end elements.
594 * @param resource $expat Parser handle.
595 * @param string $text Usually output messages.
598 function _addContent($expat, $text) {
599 if ($this->_in_content_tag
) {
600 $this->_content
.= $text;
606 * XML and Doctype handler. Discards all such content.
607 * @param resource $expat Parser handle.
608 * @param string $default Text of default content.
611 function _default($expat, $default) {