Source for file xml.php

Documentation is available at xml.php

  1. <?php
  2. /**
  3.  *  base include file for SimpleTest
  4.  *  @package    SimpleTest
  5.  *  @subpackage UnitTester
  6.  *  @version    $Id: xml.php 1723 2008-04-08 00:34:10Z lastcraft $
  7.  */
  8.  
  9. /**#@+
  10.  *  include other SimpleTest class files
  11.  */
  12. require_once(dirname(__FILE__'/scorer.php');
  13. /**#@-*/
  14.  
  15.  *    Creates the XML needed for remote communication
  16.  *    by SimpleTest.
  17.  *    @package SimpleTest
  18.  *    @subpackage UnitTester
  19.  */
  20. class XmlReporter extends SimpleReporter {
  21.     var $_indent;
  22.     var $_namespace;
  23.  
  24.     /**
  25.      *    Sets up indentation and namespace.
  26.      *    @param string $namespace        Namespace to add to each tag.
  27.      *    @param string $indent           Indenting to add on each nesting.
  28.      *    @access public
  29.      */
  30.     function XmlReporter($namespace false$indent '  '{
  31.         $this->SimpleReporter();
  32.         $this->_namespace = ($namespace $namespace ':' '');
  33.         $this->_indent = $indent;
  34.     }
  35.  
  36.     /**
  37.      *    Calculates the pretty printing indent level
  38.      *    from the current level of nesting.
  39.      *    @param integer $offset  Extra indenting level.
  40.      *    @return string          Leading space.
  41.      *    @access protected
  42.      */
  43.     function _getIndent($offset 0{
  44.         return str_repeat(
  45.                 $this->_indent,
  46.                 count($this->getTestList()) $offset);
  47.     }
  48.  
  49.     /**
  50.      *    Converts character string to parsed XML
  51.      *    entities string.
  52.      *    @param string text        Unparsed character data.
  53.      *    @return string            Parsed character data.
  54.      *    @access public
  55.      */
  56.     function toParsedXml($text{
  57.         return str_replace(
  58.                 array('&''<''>''"''\''),
  59.                 array('&amp;''&lt;''&gt;''&quot;''&apos;'),
  60.                 $text);
  61.     }
  62.  
  63.     /**
  64.      *    Paints the start of a group test.
  65.      *    @param string $test_name   Name of test that is starting.
  66.      *    @param integer $size       Number of test cases starting.
  67.      *    @access public
  68.      */
  69.     function paintGroupStart($test_name$size{
  70.         parent::paintGroupStart($test_name$size);
  71.         print $this->_getIndent();
  72.         print "<" $this->_namespace . "group size=\"$size\">\n";
  73.         print $this->_getIndent(1);
  74.         print "<" $this->_namespace . "name>" .
  75.                 $this->toParsedXml($test_name.
  76.                 "</" $this->_namespace . "name>\n";
  77.     }
  78.  
  79.     /**
  80.      *    Paints the end of a group test.
  81.      *    @param string $test_name   Name of test that is ending.
  82.      *    @access public
  83.      */
  84.     function paintGroupEnd($test_name{
  85.         print $this->_getIndent();
  86.         print "</" $this->_namespace . "group>\n";
  87.         parent::paintGroupEnd($test_name);
  88.     }
  89.  
  90.     /**
  91.      *    Paints the start of a test case.
  92.      *    @param string $test_name   Name of test that is starting.
  93.      *    @access public
  94.      */
  95.     function paintCaseStart($test_name{
  96.         parent::paintCaseStart($test_name);
  97.         print $this->_getIndent();
  98.         print "<" $this->_namespace . "case>\n";
  99.         print $this->_getIndent(1);
  100.         print "<" $this->_namespace . "name>" .
  101.                 $this->toParsedXml($test_name.
  102.                 "</" $this->_namespace . "name>\n";
  103.     }
  104.  
  105.     /**
  106.      *    Paints the end of a test case.
  107.      *    @param string $test_name   Name of test that is ending.
  108.      *    @access public
  109.      */
  110.     function paintCaseEnd($test_name{
  111.         print $this->_getIndent();
  112.         print "</" $this->_namespace . "case>\n";
  113.         parent::paintCaseEnd($test_name);
  114.     }
  115.  
  116.     /**
  117.      *    Paints the start of a test method.
  118.      *    @param string $test_name   Name of test that is starting.
  119.      *    @access public
  120.      */
  121.     function paintMethodStart($test_name{
  122.         parent::paintMethodStart($test_name);
  123.         print $this->_getIndent();
  124.         print "<" $this->_namespace . "test>\n";
  125.         print $this->_getIndent(1);
  126.         print "<" $this->_namespace . "name>" .
  127.                 $this->toParsedXml($test_name.
  128.                 "</" $this->_namespace . "name>\n";
  129.     }
  130.  
  131.     /**
  132.      *    Paints the end of a test method.
  133.      *    @param string $test_name   Name of test that is ending.
  134.      *    @param integer $progress   Number of test cases ending.
  135.      *    @access public
  136.      */
  137.     function paintMethodEnd($test_name{
  138.         print $this->_getIndent();
  139.         print "</" $this->_namespace . "test>\n";
  140.         parent::paintMethodEnd($test_name);
  141.     }
  142.  
  143.     /**
  144.      *    Paints pass as XML.
  145.      *    @param string $message        Message to encode.
  146.      *    @access public
  147.      */
  148.     function paintPass($message{
  149.         parent::paintPass($message);
  150.         print $this->_getIndent(1);
  151.         print "<" $this->_namespace . "pass>";
  152.         print $this->toParsedXml($message);
  153.         print "</" $this->_namespace . "pass>\n";
  154.     }
  155.  
  156.     /**
  157.      *    Paints failure as XML.
  158.      *    @param string $message        Message to encode.
  159.      *    @access public
  160.      */
  161.     function paintFail($message{
  162.         parent::paintFail($message);
  163.         print $this->_getIndent(1);
  164.         print "<" $this->_namespace . "fail>";
  165.         print $this->toParsedXml($message);
  166.         print "</" $this->_namespace . "fail>\n";
  167.     }
  168.  
  169.     /**
  170.      *    Paints error as XML.
  171.      *    @param string $message        Message to encode.
  172.      *    @access public
  173.      */
  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";
  180.     }
  181.  
  182.     /**
  183.      *    Paints exception as XML.
  184.      *    @param Exception $exception    Exception to encode.
  185.      *    @access public
  186.      */
  187.     function paintException($exception{
  188.         parent::paintException($exception);
  189.         print $this->_getIndent(1);
  190.         print "<" $this->_namespace . "exception>";
  191.         $message 'Unexpected exception of type [' get_class($exception.
  192.                 '] with message ['$exception->getMessage(.
  193.                 '] in ['$exception->getFile(.
  194.                 ' line ' $exception->getLine(']';
  195.         print $this->toParsedXml($message);
  196.         print "</" $this->_namespace . "exception>\n";
  197.     }
  198.  
  199.     /**
  200.      *    Paints the skipping message and tag.
  201.      *    @param string $message        Text to display in skip tag.
  202.      *    @access public
  203.      */
  204.     function paintSkip($message{
  205.         parent::paintSkip($message);
  206.         print $this->_getIndent(1);
  207.         print "<" $this->_namespace . "skip>";
  208.         print $this->toParsedXml($message);
  209.         print "</" $this->_namespace . "skip>\n";
  210.     }
  211.  
  212.     /**
  213.      *    Paints a simple supplementary message.
  214.      *    @param string $message        Text to display.
  215.      *    @access public
  216.      */
  217.     function paintMessage($message{
  218.         parent::paintMessage($message);
  219.         print $this->_getIndent(1);
  220.         print "<" $this->_namespace . "message>";
  221.         print $this->toParsedXml($message);
  222.         print "</" $this->_namespace . "message>\n";
  223.     }
  224.  
  225.     /**
  226.      *    Paints a formatted ASCII message such as a
  227.      *    variable dump.
  228.      *    @param string $message        Text to display.
  229.      *    @access public
  230.      */
  231.     function paintFormattedMessage($message{
  232.         parent::paintFormattedMessage($message);
  233.         print $this->_getIndent(1);
  234.         print "<" $this->_namespace . "formatted>";
  235.         print "<![CDATA[$message]]>";
  236.         print "</" $this->_namespace . "formatted>\n";
  237.     }
  238.  
  239.     /**
  240.      *    Serialises the event object.
  241.      *    @param string $type        Event type as text.
  242.      *    @param mixed $payload      Message or object.
  243.      *    @access public
  244.      */
  245.     function paintSignal($type$payload{
  246.         parent::paintSignal($type$payload);
  247.         print $this->_getIndent(1);
  248.         print "<" $this->_namespace . "signal type=\"$type\">";
  249.         print "<![CDATA[" serialize($payload"]]>";
  250.         print "</" $this->_namespace . "signal>\n";
  251.     }
  252.  
  253.     /**
  254.      *    Paints the test document header.
  255.      *    @param string $test_name     First test top level
  256.      *                                  to start.
  257.      *    @access public
  258.      *    @abstract
  259.      */
  260.     function paintHeader($test_name{
  261.         if (SimpleReporter::inCli()) {
  262.             header('Content-type: text/xml');
  263.         }
  264.         print "<?xml version=\"1.0\"";
  265.         if ($this->_namespace{
  266.             print " xmlns:" $this->_namespace .
  267.                     "=\"www.lastcraft.com/SimpleTest/Beta3/Report\"";
  268.         }
  269.         print "?>\n";
  270.         print "<" $this->_namespace . "run>\n";
  271.     }
  272.  
  273.     /**
  274.      *    Paints the test document footer.
  275.      *    @param string $test_name        The top level test.
  276.      *    @access public
  277.      *    @abstract
  278.      */
  279.     function paintFooter($test_name{
  280.         print "</" $this->_namespace . "run>\n";
  281.     }
  282. }
  283.  
  284. /**
  285.  *    Accumulator for incoming tag. Holds the
  286.  *    incoming test structure information for
  287.  *    later dispatch to the reporter.
  288.  *    @package SimpleTest
  289.  *    @subpackage UnitTester
  290.  */
  291. class NestingXmlTag {
  292.     var $_name;
  293.     var $_attributes;
  294.  
  295.     /**
  296.      *    Sets the basic test information except
  297.      *    the name.
  298.      *    @param hash $attributes   Name value pairs.
  299.      *    @access public
  300.      */
  301.     function NestingXmlTag($attributes{
  302.         $this->_name = false;
  303.         $this->_attributes = $attributes;
  304.     }
  305.  
  306.     /**
  307.      *    Sets the test case/method name.
  308.      *    @param string $name        Name of test.
  309.      *    @access public
  310.      */
  311.     function setName($name{
  312.         $this->_name = $name;
  313.     }
  314.  
  315.     /**
  316.      *    Accessor for name.
  317.      *    @return string        Name of test.
  318.      *    @access public
  319.      */
  320.     function getName({
  321.         return $this->_name;
  322.     }
  323.  
  324.     /**
  325.      *    Accessor for attributes.
  326.      *    @return hash        All attributes.
  327.      *    @access protected
  328.      */
  329.     function _getAttributes({
  330.         return $this->_attributes;
  331.     }
  332. }
  333.  
  334. /**
  335.  *    Accumulator for incoming method tag. Holds the
  336.  *    incoming test structure information for
  337.  *    later dispatch to the reporter.
  338.  *    @package SimpleTest
  339.  *    @subpackage UnitTester
  340.  */
  341. class NestingMethodTag extends NestingXmlTag {
  342.  
  343.     /**
  344.      *    Sets the basic test information except
  345.      *    the name.
  346.      *    @param hash $attributes   Name value pairs.
  347.      *    @access public
  348.      */
  349.     function NestingMethodTag($attributes{
  350.         $this->NestingXmlTag($attributes);
  351.     }
  352.  
  353.     /**
  354.      *    Signals the appropriate start event on the
  355.      *    listener.
  356.      *    @param SimpleReporter $listener    Target for events.
  357.      *    @access public
  358.      */
  359.     function paintStart(&$listener{
  360.         $listener->paintMethodStart($this->getName());
  361.     }
  362.  
  363.     /**
  364.      *    Signals the appropriate end event on the
  365.      *    listener.
  366.      *    @param SimpleReporter $listener    Target for events.
  367.      *    @access public
  368.      */
  369.     function paintEnd(&$listener{
  370.         $listener->paintMethodEnd($this->getName());
  371.     }
  372. }
  373.  
  374. /**
  375.  *    Accumulator for incoming case tag. Holds the
  376.  *    incoming test structure information for
  377.  *    later dispatch to the reporter.
  378.  *    @package SimpleTest
  379.  *    @subpackage UnitTester
  380.  */
  381. class NestingCaseTag extends NestingXmlTag {
  382.  
  383.     /**
  384.      *    Sets the basic test information except
  385.      *    the name.
  386.      *    @param hash $attributes   Name value pairs.
  387.      *    @access public
  388.      */
  389.     function NestingCaseTag($attributes{
  390.         $this->NestingXmlTag($attributes);
  391.     }
  392.  
  393.     /**
  394.      *    Signals the appropriate start event on the
  395.      *    listener.
  396.      *    @param SimpleReporter $listener    Target for events.
  397.      *    @access public
  398.      */
  399.     function paintStart(&$listener{
  400.         $listener->paintCaseStart($this->getName());
  401.     }
  402.  
  403.     /**
  404.      *    Signals the appropriate end event on the
  405.      *    listener.
  406.      *    @param SimpleReporter $listener    Target for events.
  407.      *    @access public
  408.      */
  409.     function paintEnd(&$listener{
  410.         $listener->paintCaseEnd($this->getName());
  411.     }
  412. }
  413.  
  414. /**
  415.  *    Accumulator for incoming group tag. Holds the
  416.  *    incoming test structure information for
  417.  *    later dispatch to the reporter.
  418.  *    @package SimpleTest
  419.  *    @subpackage UnitTester
  420.  */
  421. class NestingGroupTag extends NestingXmlTag {
  422.  
  423.     /**
  424.      *    Sets the basic test information except
  425.      *    the name.
  426.      *    @param hash $attributes   Name value pairs.
  427.      *    @access public
  428.      */
  429.     function NestingGroupTag($attributes{
  430.         $this->NestingXmlTag($attributes);
  431.     }
  432.  
  433.     /**
  434.      *    Signals the appropriate start event on the
  435.      *    listener.
  436.      *    @param SimpleReporter $listener    Target for events.
  437.      *    @access public
  438.      */
  439.     function paintStart(&$listener{
  440.         $listener->paintGroupStart($this->getName()$this->getSize());
  441.     }
  442.  
  443.     /**
  444.      *    Signals the appropriate end event on the
  445.      *    listener.
  446.      *    @param SimpleReporter $listener    Target for events.
  447.      *    @access public
  448.      */
  449.     function paintEnd(&$listener{
  450.         $listener->paintGroupEnd($this->getName());
  451.     }
  452.  
  453.     /**
  454.      *    The size in the attributes.
  455.      *    @return integer     Value of size attribute or zero.
  456.      *    @access public
  457.      */
  458.     function getSize({
  459.         $attributes $this->_getAttributes();
  460.         if (isset($attributes['SIZE'])) {
  461.             return (integer)$attributes['SIZE'];
  462.         }
  463.         return 0;
  464.     }
  465. }
  466.  
  467. /**
  468.  *    Parser for importing the output of the XmlReporter.
  469.  *    Dispatches that output to another reporter.
  470.  *    @package SimpleTest
  471.  *    @subpackage UnitTester
  472.  */
  473.     var $_listener;
  474.     var $_expat;
  475.     var $_tag_stack;
  476.     var $_in_content_tag;
  477.     var $_content;
  478.     var $_attributes;
  479.  
  480.     /**
  481.      *    Loads a listener with the SimpleReporter
  482.      *    interface.
  483.      *    @param SimpleReporter $listener   Listener of tag events.
  484.      *    @access public
  485.      */
  486.     function SimpleTestXmlParser(&$listener{
  487.         $this->_listener = &$listener;
  488.         $this->_expat = &$this->_createParser();
  489.         $this->_tag_stack = array();
  490.         $this->_in_content_tag = false;
  491.         $this->_content = '';
  492.         $this->_attributes = array();
  493.     }
  494.  
  495.     /**
  496.      *    Parses a block of XML sending the results to
  497.      *    the listener.
  498.      *    @param string $chunk        Block of text to read.
  499.      *    @return boolean             True if valid XML.
  500.      *    @access public
  501.      */
  502.     function parse($chunk{
  503.         if (xml_parse($this->_expat$chunk)) {
  504.             trigger_error('XML parse error with ' .
  505.                     xml_error_string(xml_get_error_code($this->_expat)));
  506.             return false;
  507.         }
  508.         return true;
  509.     }
  510.  
  511.     /**
  512.      *    Sets up expat as the XML parser.
  513.      *    @return resource        Expat handle.
  514.      *    @access protected
  515.      */
  516.     function &_createParser({
  517.         $expat xml_parser_create();
  518.         xml_set_object($expat$this);
  519.         xml_set_element_handler($expat'_startElement''_endElement');
  520.         xml_set_character_data_handler($expat'_addContent');
  521.         xml_set_default_handler($expat'_default');
  522.         return $expat;
  523.     }
  524.  
  525.     /**
  526.      *    Opens a new test nesting level.
  527.      *    @return NestedXmlTag     The group, case or method tag
  528.      *                              to start.
  529.      *    @access private
  530.      */
  531.     function _pushNestingTag($nested{
  532.         array_unshift($this->_tag_stack$nested);
  533.     }
  534.  
  535.     /**
  536.      *    Accessor for current test structure tag.
  537.      *    @return NestedXmlTag     The group, case or method tag
  538.      *                              being parsed.
  539.      *    @access private
  540.      */
  541.     function &_getCurrentNestingTag({
  542.         return $this->_tag_stack[0];
  543.     }
  544.  
  545.     /**
  546.      *    Ends a nesting tag.
  547.      *    @return NestedXmlTag     The group, case or method tag
  548.      *                              just finished.
  549.      *    @access private
  550.      */
  551.     function _popNestingTag({
  552.         return array_shift($this->_tag_stack);
  553.     }
  554.  
  555.     /**
  556.      *    Test if tag is a leaf node with only text content.
  557.      *    @param string $tag        XML tag name.
  558.      *    @return @boolean          True if leaf, false if nesting.
  559.      *    @private
  560.      */
  561.     function _isLeaf($tag{
  562.         return in_array($tagarray(
  563.                 'NAME''PASS''FAIL''EXCEPTION''SKIP''MESSAGE''FORMATTED''SIGNAL'));
  564.     }
  565.  
  566.     /**
  567.      *    Handler for start of event element.
  568.      *    @param resource $expat     Parser handle.
  569.      *    @param string $tag         Element name.
  570.      *    @param hash $attributes    Name value pairs.
  571.      *                                Attributes without content
  572.      *                                are marked as true.
  573.      *    @access protected
  574.      */
  575.     function _startElement($expat$tag$attributes{
  576.         $this->_attributes = $attributes;
  577.         if ($tag == 'GROUP'{
  578.             $this->_pushNestingTag(new NestingGroupTag($attributes));
  579.         elseif ($tag == 'CASE'{
  580.             $this->_pushNestingTag(new NestingCaseTag($attributes));
  581.         elseif ($tag == 'TEST'{
  582.             $this->_pushNestingTag(new NestingMethodTag($attributes));
  583.         elseif ($this->_isLeaf($tag)) {
  584.             $this->_in_content_tag = true;
  585.             $this->_content = '';
  586.         }
  587.     }
  588.  
  589.     /**
  590.      *    End of element event.
  591.      *    @param resource $expat     Parser handle.
  592.      *    @param string $tag         Element name.
  593.      *    @access protected
  594.      */
  595.     function _endElement($expat$tag{
  596.         $this->_in_content_tag = false;
  597.         if (in_array($tagarray('GROUP''CASE''TEST'))) {
  598.             $nesting_tag $this->_popNestingTag();
  599.             $nesting_tag->paintEnd($this->_listener);
  600.         elseif ($tag == 'NAME'{
  601.             $nesting_tag &$this->_getCurrentNestingTag();
  602.             $nesting_tag->setName($this->_content);
  603.             $nesting_tag->paintStart($this->_listener);
  604.         elseif ($tag == 'PASS'{
  605.             $this->_listener->paintPass($this->_content);
  606.         elseif ($tag == 'FAIL'{
  607.             $this->_listener->paintFail($this->_content);
  608.         elseif ($tag == 'EXCEPTION'{
  609.             $this->_listener->paintError($this->_content);
  610.         elseif ($tag == 'SKIP'{
  611.             $this->_listener->paintSkip($this->_content);
  612.         elseif ($tag == 'SIGNAL'{
  613.             $this->_listener->paintSignal(
  614.                     $this->_attributes['TYPE'],
  615.                     unserialize($this->_content));
  616.         elseif ($tag == 'MESSAGE'{
  617.             $this->_listener->paintMessage($this->_content);
  618.         elseif ($tag == 'FORMATTED'{
  619.             $this->_listener->paintFormattedMessage($this->_content);
  620.         }
  621.     }
  622.  
  623.     /**
  624.      *    Content between start and end elements.
  625.      *    @param resource $expat     Parser handle.
  626.      *    @param string $text        Usually output messages.
  627.      *    @access protected
  628.      */
  629.     function _addContent($expat$text{
  630.         if ($this->_in_content_tag{
  631.             $this->_content .= $text;
  632.         }
  633.         return true;
  634.     }
  635.  
  636.     /**
  637.      *    XML and Doctype handler. Discards all such content.
  638.      *    @param resource $expat     Parser handle.
  639.      *    @param string $default     Text of default content.
  640.      *    @access protected
  641.      */
  642.     function _default($expat$default{
  643.     }
  644. }
  645. ?>

Documentation generated on Sun, 04 May 2008 09:22:31 -0500 by phpDocumentor 1.3.0