MDL-15476
[moodle-linuxchix.git] / lib / simpletestlib / xml.php
blob0de9a5ebe82984f6b94eb3d399a730a7c5608f3d
1 <?php
2 /**
3 * base include file for SimpleTest
4 * @package SimpleTest
5 * @subpackage UnitTester
6 * @version $Id$
7 */
9 /**#@+
10 * include other SimpleTest class files
12 require_once(dirname(__FILE__) . '/scorer.php');
13 /**#@-*/
15 /**
16 * Creates the XML needed for remote communication
17 * by SimpleTest.
18 * @package SimpleTest
19 * @subpackage UnitTester
21 class XmlReporter extends SimpleReporter {
22 var $_indent;
23 var $_namespace;
25 /**
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.
29 * @access public
31 function XmlReporter($namespace = false, $indent = ' ') {
32 $this->SimpleReporter();
33 $this->_namespace = ($namespace ? $namespace . ':' : '');
34 $this->_indent = $indent;
37 /**
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.
42 * @access protected
44 function _getIndent($offset = 0) {
45 return str_repeat(
46 $this->_indent,
47 count($this->getTestList()) + $offset);
50 /**
51 * Converts character string to parsed XML
52 * entities string.
53 * @param string text Unparsed character data.
54 * @return string Parsed character data.
55 * @access public
57 function toParsedXml($text) {
58 return str_replace(
59 array('&', '<', '>', '"', '\''),
60 array('&amp;', '&lt;', '&gt;', '&quot;', '&apos;'),
61 $text);
64 /**
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.
68 * @access public
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";
80 /**
81 * Paints the end of a group test.
82 * @param string $test_name Name of test that is ending.
83 * @access public
85 function paintGroupEnd($test_name) {
86 print $this->_getIndent();
87 print "</" . $this->_namespace . "group>\n";
88 parent::paintGroupEnd($test_name);
91 /**
92 * Paints the start of a test case.
93 * @param string $test_name Name of test that is starting.
94 * @access public
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.
109 * @access public
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.
120 * @access public
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.
136 * @access public
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.
147 * @access public
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.
160 * @access public
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.
173 * @access public
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.
186 * @access public
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.
203 * @access public
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.
216 * @access public
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
228 * variable dump.
229 * @param string $message Text to display.
230 * @access public
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.
244 * @access public
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
257 * to start.
258 * @access public
259 * @abstract
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\"";
270 print "?>\n";
271 print "<" . $this->_namespace . "run>\n";
275 * Paints the test document footer.
276 * @param string $test_name The top level test.
277 * @access public
278 * @abstract
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 {
293 var $_name;
294 var $_attributes;
297 * Sets the basic test information except
298 * the name.
299 * @param hash $attributes Name value pairs.
300 * @access public
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.
310 * @access public
312 function setName($name) {
313 $this->_name = $name;
317 * Accessor for name.
318 * @return string Name of test.
319 * @access public
321 function getName() {
322 return $this->_name;
326 * Accessor for attributes.
327 * @return hash All attributes.
328 * @access protected
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
346 * the name.
347 * @param hash $attributes Name value pairs.
348 * @access public
350 function NestingMethodTag($attributes) {
351 $this->NestingXmlTag($attributes);
355 * Signals the appropriate start event on the
356 * listener.
357 * @param SimpleReporter $listener Target for events.
358 * @access public
360 function paintStart(&$listener) {
361 $listener->paintMethodStart($this->getName());
365 * Signals the appropriate end event on the
366 * listener.
367 * @param SimpleReporter $listener Target for events.
368 * @access public
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
386 * the name.
387 * @param hash $attributes Name value pairs.
388 * @access public
390 function NestingCaseTag($attributes) {
391 $this->NestingXmlTag($attributes);
395 * Signals the appropriate start event on the
396 * listener.
397 * @param SimpleReporter $listener Target for events.
398 * @access public
400 function paintStart(&$listener) {
401 $listener->paintCaseStart($this->getName());
405 * Signals the appropriate end event on the
406 * listener.
407 * @param SimpleReporter $listener Target for events.
408 * @access public
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
426 * the name.
427 * @param hash $attributes Name value pairs.
428 * @access public
430 function NestingGroupTag($attributes) {
431 $this->NestingXmlTag($attributes);
435 * Signals the appropriate start event on the
436 * listener.
437 * @param SimpleReporter $listener Target for events.
438 * @access public
440 function paintStart(&$listener) {
441 $listener->paintGroupStart($this->getName(), $this->getSize());
445 * Signals the appropriate end event on the
446 * listener.
447 * @param SimpleReporter $listener Target for events.
448 * @access public
450 function paintEnd(&$listener) {
451 $listener->paintGroupEnd($this->getName());
455 * The size in the attributes.
456 * @return integer Value of size attribute or zero.
457 * @access public
459 function getSize() {
460 $attributes = $this->_getAttributes();
461 if (isset($attributes['SIZE'])) {
462 return (integer)$attributes['SIZE'];
464 return 0;
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 {
475 var $_listener;
476 var $_expat;
477 var $_tag_stack;
478 var $_in_content_tag;
479 var $_content;
480 var $_attributes;
483 * Loads a listener with the SimpleReporter
484 * interface.
485 * @param SimpleReporter $listener Listener of tag events.
486 * @access public
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
499 * the listener.
500 * @param string $chunk Block of text to read.
501 * @return boolean True if valid XML.
502 * @access public
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)));
508 return false;
510 return true;
514 * Sets up expat as the XML parser.
515 * @return resource Expat handle.
516 * @access protected
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');
524 return $expat;
528 * Opens a new test nesting level.
529 * @return NestedXmlTag The group, case or method tag
530 * to start.
531 * @access private
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
540 * being parsed.
541 * @access private
543 function &_getCurrentNestingTag() {
544 return $this->_tag_stack[0];
548 * Ends a nesting tag.
549 * @return NestedXmlTag The group, case or method tag
550 * just finished.
551 * @access private
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.
561 * @private
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.
575 * @access protected
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.
595 * @access protected
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.
629 * @access protected
631 function _addContent($expat, $text) {
632 if ($this->_in_content_tag) {
633 $this->_content .= $text;
635 return true;
639 * XML and Doctype handler. Discards all such content.
640 * @param resource $expat Parser handle.
641 * @param string $default Text of default content.
642 * @access protected
644 function _default($expat, $default) {