Upgraded simpletestlib from 1.0.1alpha3 to 1.0.1beta
authornicolasconnault <nicolasconnault>
Thu, 26 Apr 2007 07:11:44 +0000 (07:11 +0000)
committernicolasconnault <nicolasconnault>
Thu, 26 Apr 2007 07:11:44 +0000 (07:11 +0000)
27 files changed:
lib/simpletestlib/HELP_MY_TESTS_DONT_WORK_ANYMORE
lib/simpletestlib/LICENSE
lib/simpletestlib/README
lib/simpletestlib/VERSION
lib/simpletestlib/browser.php
lib/simpletestlib/collector.php
lib/simpletestlib/compatibility.php
lib/simpletestlib/dumper.php
lib/simpletestlib/encoding.php
lib/simpletestlib/errors.php
lib/simpletestlib/exceptions.php
lib/simpletestlib/expectation.php
lib/simpletestlib/extensions/pear_test_case.php
lib/simpletestlib/extensions/phpunit_test_case.php
lib/simpletestlib/mock_objects.php
lib/simpletestlib/parser.php
lib/simpletestlib/reflection_php5.php
lib/simpletestlib/remote.php
lib/simpletestlib/reporter.php
lib/simpletestlib/scorer.php
lib/simpletestlib/shell_tester.php
lib/simpletestlib/simpletest.php
lib/simpletestlib/test_case.php
lib/simpletestlib/unit_tester.php
lib/simpletestlib/url.php
lib/simpletestlib/web_tester.php
lib/simpletestlib/xml.php

index 2c9ae6f5b1c87e2bafec9385ae82eb5b85520fb5..806a7a7056b03c03ac982affcbf49e62cb37822f 100644 (file)
@@ -5,6 +5,28 @@ written with earlier versions will fail with the newest ones. The most
 dramatic changes are in the alpha releases. Here is a list of possible
 problems and their fixes...
 
+No method _getTest() on mocks
+-----------------------------
+This has finally been removed. It was a pretty esoteric
+flex point anyway. It was there to allow the mocks to
+work with other test tools, but no one does this anyway.
+
+No method assertError(), assertNoErrors(), swallowErrors()
+----------------------------------------------------------
+These have been deprecated in 1.0.1beta in favour of
+expectError() and expectException(). assertNoErrors() is
+redundant if you use expectError() as failures are now reporterted
+immediately.
+
+No method TestCase::signal()
+----------------------------
+This has been deprecated in favour of triggering an error or
+throwing an exception. Deprecated as of 1.0.1beta.
+
+No method TestCase::sendMessage()
+---------------------------------
+This has been deprecated as of 1.0.1beta.
+
 Failure to connect now emits failures
 -------------------------------------
 It used to be that you would have to use the
@@ -46,7 +68,7 @@ No method addPartialMockCode()
 ------------------------------
 The ability to insert arbitrary partial mock code
 has been removed. This was a low value feature
-causing needless complications.It was removed
+causing needless complications. It was removed
 in the 1.0.1beta release.
 
 No method setMockBaseClass()
@@ -139,7 +161,7 @@ My custom test case ignored by tally()
 The _assertTrue method has had it's signature changed due to a bug
 in the PHP 5.0.1 release. You must now use getTest() from within
 that method to get the test case. Mock compatibility with other
-unit testers is now deprecated as of 1.0.1alpha as PEAR::PHUnit2
+unit testers is now deprecated as of 1.0.1alpha as PEAR::PHPUnit2
 should soon have mock support of it's own.
 
 Broken code extending SimpleRunner
index 4a1f72014ec14de56cc3fd2e21c6071564e6f574..09f465ab703f79f8c7b509795d785b1124889f86 100644 (file)
@@ -2,7 +2,7 @@
                       Version 2.1, February 1999
 
  Copyright (C) 1991, 1999 Free Software Foundation, Inc.
    51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  Everyone is permitted to copy and distribute verbatim copies
  of this license document, but changing it is not allowed.
 
@@ -485,7 +485,7 @@ convey the exclusion of warranty; and each file should have at least the
 
     You should have received a copy of the GNU Lesser General Public
     License along with this library; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 
 Also add information on how to contact you by electronic and paper mail.
 
index c2ff9b757f23db3ebb8c44efbdf95ab7261fdd55..b8d1979205d9e907fd16870e8f2e5e4bb8015401 100644 (file)
@@ -1,7 +1,7 @@
 SimpleTest
 ==========
 You probably got this package from...
-http://sourceforge.net/projects/simpletest/
+http://simpletest.sourceforge.net/projects/simpletest/
 
 If there is no licence agreement with this package please download
 a version from the location above. You must read and accept that
index 9980c7df52c5350d4d6cd903e3926ccb1fb18a17..fff574851f5b5c0d80af6fd0044222f70cc44e7a 100644 (file)
@@ -1 +1 @@
-1.0.1alpha3
\ No newline at end of file
+1.0.1beta
\ No newline at end of file
index 462262af4308e740c77e503903614cb6eead7f8b..6b41d0b4b4c7fbbe893ab097e319208203d1155e 100644 (file)
                     $form->submitButton(new SimpleById($id), $additional));
             return ($success ? $this->getContent() : $success);
         }
+        
+        /**
+         *    Tests to see if a submit button exists with this
+         *    label.
+         *    @param string $label    Button label.
+         *    @return boolean         True if present.
+         *    @access public
+         */
+        function isSubmit($label) {
+            return (boolean)$this->_page->getFormBySubmit(new SimpleByLabel($label));
+        }
 
         /**
          *    Clicks the submit image by some kind of label. Usually
                     $form->submitImage(new SimpleById($id), $x, $y, $additional));
             return ($success ? $this->getContent() : $success);
         }
+        
+        /**
+         *    Tests to see if an image exists with this
+         *    title or alt text.
+         *    @param string $label    Image text.
+         *    @return boolean         True if present.
+         *    @access public
+         */
+        function isImage($label) {
+            return (boolean)$this->_page->getFormByImage(new SimpleByLabel($label));
+        }
 
         /**
          *    Submits a form by the ID.
         }
 
         /**
-         *    Follows a link by label. Will click the first link
+         *    Finds a URL by label. Will find the first link
          *    found with this link text by default, or a later
          *    one if an index is given. The match ignores case and
          *    white space issues.
          *    @param string $label     Text between the anchor tags.
          *    @param integer $index    Link position counting from zero.
-         *    @return string/boolean   Page on success.
+         *    @return string/boolean   URL on success.
          *    @access public
          */
-        function clickLink($label, $index = 0) {
+        function getLink($label, $index = 0) {
             $urls = $this->_page->getUrlsByLabel($label);
             if (count($urls) == 0) {
                 return false;
             if (count($urls) < $index + 1) {
                 return false;
             }
-            $this->_load($urls[$index], new SimpleGetEncoding());
-            return $this->getContent();
+            return $urls[$index];
         }
 
         /**
-         *    Tests to see if a link is present by label.
-         *    @param string $label     Text of value attribute.
-         *    @return boolean          True if link present.
+         *    Follows a link by label. Will click the first link
+         *    found with this link text by default, or a later
+         *    one if an index is given. The match ignores case and
+         *    white space issues.
+         *    @param string $label     Text between the anchor tags.
+         *    @param integer $index    Link position counting from zero.
+         *    @return string/boolean   Page on success.
+         *    @access public
+         */
+        function clickLink($label, $index = 0) {
+            $url = $this->getLink($label, $index);
+            if ($url === false) {
+                return false;
+            }
+            $this->_load($url, new SimpleGetEncoding());
+            return $this->getContent();
+        }
+        
+        /**
+         *    Finds a link by id attribute.
+         *    @param string $id        ID attribute value.
+         *    @return string/boolean   URL on success.
          *    @access public
          */
-        function isLink($label) {
-            return (count($this->_page->getUrlsByLabel($label)) > 0);
+        function getLinkById($id) {
+            return $this->_page->getUrlById($id);
         }
 
         /**
          *    @access public
          */
         function clickLinkById($id) {
-            if (! ($url = $this->_page->getUrlById($id))) {
+            if (! ($url = $this->getLinkById($id))) {
                 return false;
             }
             $this->_load($url, new SimpleGetEncoding());
             return $this->getContent();
         }
 
-        /**
-         *    Tests to see if a link is present by ID attribute.
-         *    @param string $id     Text of id attribute.
-         *    @return boolean       True if link present.
-         *    @access public
-         */
-        function isLinkById($id) {
-            return (boolean)$this->_page->getUrlById($id);
-        }
-
         /**
          *    Clicks a visible text item. Will first try buttons,
          *    then links and then images.
             }
             return $raw;
         }
+
+        /**
+         *    Tests to see if a click target exists.
+         *    @param string $label    Visible text or alt text.
+         *    @return boolean         True if target present.
+         *    @access public
+         */
+        function isClickable($label) {
+            return $this->isSubmit($label) || ($this->getLink($label) !== false) || $this->isImage($label);
+        }
     }
 ?>
\ No newline at end of file
index 34229d8064a6450aca9a55fa3f4aa565ea55a72e..439f6b5e9dc56dc6429506b6bcd57a827b840068 100644 (file)
 <?php
-/**
- * This file contains the following classes: {@link SimpleCollector}, 
- * {@link SimplePatternCollector}.
- * 
- * @author Travis Swicegood <development@domain51.com>
- * @package SimpleTest
- * @subpackage UnitTester
- * @version $Id$
- */
-
-/**
- * The basic collector for {@link GroupTest}
- *
- * @see collect(), GroupTest::collect()
- * @package SimpleTest
- * @subpackage UnitTester
- */
-class SimpleCollector {
-    
     /**
-     * Strips off any kind of slash at the end so as to normalise the path
+     * This file contains the following classes: {@link SimpleCollector},
+     * {@link SimplePatternCollector}.
      *
-     * @param string $path    Path to normalise.
+     * @author Travis Swicegood <development@domain51.com>
+     * @package SimpleTest
+     * @subpackage UnitTester
+     * @version $Id$
      */
-    function _removeTrailingSlash($path) {
-        return preg_replace('|[\\/]$|', '', $path);
-        
-       /**
-        * @internal
-        * Try benchmarking the following.  It's more code, but by not using the
-        * regex, it may be faster?  Also, shouldn't be looking for 
-        * DIRECTORY_SEPERATOR instead of a manual "/"? 
-        */
-        if (substr($path, -1) == DIRECTORY_SEPERATOR) {
-            return substr($path, 0, -1);
-        } else {
-            return $path;
-        }
-    }
-
+    
     /**
-     * Scans the directory and adds what it can.
-     * @param object $test    Group test with {@link GroupTest::addTestFile()} method.
-     * @param string $path    Directory to scan.
-     * @see _attemptToAdd()
+     * The basic collector for {@link GroupTest}
+     *
+     * @see collect(), GroupTest::collect()
+     * @package SimpleTest
+     * @subpackage UnitTester
      */
-    function collect(&$test, $path) {
-        $path = $this->_removeTrailingSlash($path);
-        if ($handle = opendir($path)) {
-            while (($entry = readdir($handle)) !== false) {
-                $this->_handle($test, $path . DIRECTORY_SEPARATOR . $entry);
+    class SimpleCollector {
+    
+        /**
+         * Strips off any kind of slash at the end so as to normalise the path.
+         * @param string $path    Path to normalise.
+         * @return string         Path without trailing slash.
+         */
+        function _removeTrailingSlash($path) {
+            if (substr($path, -1) == DIRECTORY_SEPARATOR) {
+                return substr($path, 0, -1);
+            } elseif (substr($path, -1) == '/') {
+                return substr($path, 0, -1);
+            } else {
+                return $path;
             }
-            closedir($handle);
         }
-    }
     
-    /**
-     * This method determines what should be done with a given file and adds
-     * it via {@link GroupTest::addTestFile()} if necessary.
-     *
-     * This method should be overriden to provide custom matching criteria, 
-     * such as pattern matching, recursive matching, etc.  For an example, see
-     * {@link SimplePatternCollector::_handle()}.
-     *
-     * @param object $test      Group test with {@link GroupTest::addTestFile()} method.
-     * @param string $filename  A filename as generated by {@link collect()}
-     * @see collect()
-     * @access protected
-     */
-    function _handle(&$test, $file) {
-        if (!is_dir($file)) {
-            $test->addTestFile($file);
+        /**
+         * Scans the directory and adds what it can.
+         * @param object $test    Group test with {@link GroupTest::addTestFile()} method.
+         * @param string $path    Directory to scan.
+         * @see _attemptToAdd()
+         */
+        function collect(&$test, $path) {
+            $path = $this->_removeTrailingSlash($path);
+            if ($handle = opendir($path)) {
+                while (($entry = readdir($handle)) !== false) {
+                    $this->_handle($test, $path . DIRECTORY_SEPARATOR . $entry);
+                }
+                closedir($handle);
+            }
         }
-    }
-}
-
-/**
- * An extension to {@link SimpleCollector} that only adds files matching a
- * given pattern.
- *
- * @package SimpleTest
- * @subpackage UnitTester
- * @see SimpleCollector
- */
-class SimplePatternCollector extends SimpleCollector {
-    var $_pattern;
     
+        /**
+         * This method determines what should be done with a given file and adds
+         * it via {@link GroupTest::addTestFile()} if necessary.
+         *
+         * This method should be overriden to provide custom matching criteria,
+         * such as pattern matching, recursive matching, etc.  For an example, see
+         * {@link SimplePatternCollector::_handle()}.
+         *
+         * @param object $test      Group test with {@link GroupTest::addTestFile()} method.
+         * @param string $filename  A filename as generated by {@link collect()}
+         * @see collect()
+         * @access protected
+         */
+        function _handle(&$test, $file) {
+            if (! is_dir($file)) {
+                $test->addTestFile($file);
+            }
+        }
+    }
     
     /**
+     * An extension to {@link SimpleCollector} that only adds files matching a
+     * given pattern.
      *
-     * @param string $pattern   Perl compatible regex to test name against
-     *  See {@link http://us4.php.net/manual/en/reference.pcre.pattern.syntax.php PHP's PCRE}
-     *  for full documentation of valid pattern.s
+     * @package SimpleTest
+     * @subpackage UnitTester
+     * @see SimpleCollector
      */
-    function SimplePatternCollector($pattern = '/php$/i') {
-        $this->_pattern = $pattern;
-    }
+    class SimplePatternCollector extends SimpleCollector {
+        var $_pattern;
     
+        /**
+         *
+         * @param string $pattern   Perl compatible regex to test name against
+         *  See {@link http://us4.php.net/manual/en/reference.pcre.pattern.syntax.php PHP's PCRE}
+         *  for full documentation of valid pattern.s
+         */
+        function SimplePatternCollector($pattern = '/php$/i') {
+            $this->_pattern = $pattern;
+        }
     
-    /**
-     * Attempts to add files that match a given pattern.
-     *
-     * @see SimpleCollector::_handle()
-     * @param object $test    Group test with {@link GroupTest::addTestFile()} method.
-     * @param string $path    Directory to scan.
-     * @access protected
-     */
-    function _handle(&$test, $filename) {
-        if (preg_match($this->_pattern, $filename)) {
-            parent::_handle($test, $filename);
+        /**
+         * Attempts to add files that match a given pattern.
+         *
+         * @see SimpleCollector::_handle()
+         * @param object $test    Group test with {@link GroupTest::addTestFile()} method.
+         * @param string $path    Directory to scan.
+         * @access protected
+         */
+        function _handle(&$test, $filename) {
+            if (preg_match($this->_pattern, $filename)) {
+                parent::_handle($test, $filename);
+            }
         }
     }
-}
 ?>
\ No newline at end of file
index 67b806a948a32cf27425b6867828097d53a9de0c..eefb6029b873badd52da8336fa3fa95b425b3872 100644 (file)
@@ -70,6 +70,9 @@
             if (is_array($first) && is_array($second)) {
                 return SimpleTestCompatibility::_isArrayOfIdenticalTypes($first, $second);
             }
+            if ($first !== $second) {
+                return false;
+            }
             return true;
         }
         
          *    @static
          */
         function isReference(&$first, &$second) {
-            if (version_compare(phpversion(), '5', '>=')
-                   && is_object($first)) {
+            if (version_compare(phpversion(), '5', '>=') && is_object($first)) {
                    return ($first === $second);
                }
                if (is_object($first) && is_object($second)) {
          *    @static
          */
         function isA($object, $class) {
-            if (function_exists('is_a')) {
-                return is_a($object, $class);
-            }
             if (version_compare(phpversion(), '5') >= 0) {
                 if (! class_exists($class, false)) {
                     if (function_exists('interface_exists')) {
                 eval("\$is_a = \$object instanceof $class;");
                 return $is_a;
             }
+            if (function_exists('is_a')) {
+                return is_a($object, $class);
+            }
             return ((strtolower($class) == get_class($object))
                     or (is_subclass_of($object, $class)));
         }
                 set_socket_timeout($handle, $timeout, 0);
             }
         }
-        
-        /**
-         *    Gets the current stack trace topmost first.
-         *    @return array        List of stack frames.
-         *    @access public
-         *    @static
-         */
-        function getStackTrace() {
-            if (function_exists('debug_backtrace')) {
-                return array_reverse(debug_backtrace());
-            }
-            return array();
-        }
     }
-?>
+?>
\ No newline at end of file
index 63af5ea86c6ae395599399c0c3777a658261ba22..ea21e6455d59b892fec3e1e4532cd73b470d9a84 100644 (file)
             ob_end_clean();
             return $formatted;
         }
-
-        /**
-         *    Extracts the last assertion that was not within
-         *    Simpletest itself. The name must start with "assert".
-         *    @param array $stack      List of stack frames.
-         *    @access public
-         *    @static
-         */
-        function getFormattedAssertionLine($stack) {
-            foreach ($stack as $frame) {
-                if (isset($frame['file'])) {
-                    if (strpos($frame['file'], SIMPLE_TEST) !== false) {
-                        if (dirname($frame['file']) . '/' == SIMPLE_TEST) {
-                            continue;
-                        }
-                    }
-                }
-                if (SimpleDumper::_stackFrameIsAnAssertion($frame)) {
-                    return ' at [' . $frame['file'] . ' line ' . $frame['line'] . ']';
-                }
-            }
-            return '';
-        }
-        
-        /**
-         *    Tries to determine if the method call is an assertion.
-         *    @param array $frame     PHP stack frame.
-         *    @access private
-         *    @static
-         */
-        function _stackFrameIsAnAssertion($frame) {
-            if (($frame['function'] == 'fail') || ($frame['function'] == 'pass')) {
-                return true;
-            }
-            if (strncmp($frame['function'], 'assert', 6) == 0) {
-                return true;
-            }
-            if (strncmp($frame['function'], 'expect', 6) == 0) {
-                return true;
-            }
-            return false;
-        }
     }
 ?>
\ No newline at end of file
index a1a0d764a441cf1265c0c3192a7e890599bcc86c..bffa0713199035dd11735c7d2b784a0acaa675c5 100644 (file)
@@ -37,7 +37,7 @@
          *    @access public
          */
         function asRequest() {
-            return $this->_key . '=' . urlencode($this->_value);
+            return urlencode($this->_key) . '=' . urlencode($this->_value);
         }
         
         /**
index 99b50962a051ba54e8c4f301526475a00bf17c75..76745f88a891563b240e1ae6669874f91c14157a 100644 (file)
@@ -15,6 +15,8 @@
      * Includes SimpleTest files.
      */
     require_once(dirname(__FILE__) . '/invoker.php');
+    require_once(dirname(__FILE__) . '/test_case.php');
+    require_once(dirname(__FILE__) . '/expectation.php');
 
     /**
      *    Extension that traps errors into an error queue.
          *    @access public
          */
         function invoke($method) {
-            set_error_handler('simpleTestErrorHandler');
+            $context = &SimpleTest::getContext();
+            $queue = &$context->get('SimpleErrorQueue');
+            $queue->setTestCase($this->GetTestCase());
+            set_error_handler('SimpleTestErrorHandler');
             parent::invoke($method);
-            $queue = &SimpleErrorQueue::instance();
-            while (list($severity, $message, $file, $line, $globals) = $queue->extract()) {
+            while (list($severity, $message, $file, $line) = $queue->extract()) {
                 $severity = SimpleErrorQueue::getSeverityAsString($severity);
-                $test_case = &$this->getTestCase();
-                $test_case->error($severity, $message, $file, $line);
+                $test = &$this->getTestCase();
+                $test->error($severity, $message, $file, $line);
             }
             restore_error_handler();
         }
      */
     class SimpleErrorQueue {
         var $_queue;
+        var $_expectation_queue;
+        var $_test;
 
         /**
          *    Starts with an empty queue.
-         *    @access public
          */
         function SimpleErrorQueue() {
             $this->clear();
         }
 
+        /**
+         *    Sets the currently running test case.
+         *    @param SimpleTestCase $test    Test case to send messages to.
+         *    @access public
+         */
+        function setTestCase(&$test) {
+            $this->_test = &$test;
+        }
+
         /**
          *    Adds an error to the front of the queue.
-         *    @param $severity        PHP error code.
-         *    @param $message         Text of error.
-         *    @param $filename        File error occoured in.
-         *    @param $line            Line number of error.
-         *    @param $super_globals   Hash of PHP super global arrays.
+         *    @param integer $severity       PHP error code.
+         *    @param string $content         Text of error.
+         *    @param string $filename        File error occoured in.
+         *    @param integer $line           Line number of error.
          *    @access public
          */
-        function add($severity, $message, $filename, $line, $super_globals) {
-            array_push(
-                    $this->_queue,
-                    array($severity, $message, $filename, $line, $super_globals));
+        function add($severity, $content, $filename, $line) {
+                       $content = str_replace('%', '%%', $content);
+            if (count($this->_expectation_queue)) {
+                $this->_testLatestError($severity, $content, $filename, $line);
+            } else {
+                array_push(
+                        $this->_queue,
+                        array($severity, $content, $filename, $line));
+            }
+        }
+
+        /**
+         *    Tests the error against the most recent expected
+         *    error.
+         *    @param integer $severity       PHP error code.
+         *    @param string $content         Text of error.
+         *    @param string $filename        File error occoured in.
+         *    @param integer $line           Line number of error.
+         *    @access private
+         */
+        function _testLatestError($severity, $content, $filename, $line) {
+            list($expected, $message) = array_shift($this->_expectation_queue);
+            $severity = $this->getSeverityAsString($severity);
+            $is_match = $this->_test->assert(
+                    $expected,
+                    $content,
+                    sprintf($message, "%s -> PHP error [$content] severity [$severity] in [$filename] line [$line]"));
+            if (! $is_match) {
+                $this->_test->error($severity, $content, $filename, $line);
+            }
         }
 
         /**
          */
         function clear() {
             $this->_queue = array();
+            $this->_expectation_queue = array();
         }
 
         /**
-         *    Tests to see if the queue is empty.
-         *    @return        True if empty.
+         *    @deprecated
          */
-        function isEmpty() {
-            return (count($this->_queue) == 0);
+        function assertNoErrors($message) {
+            return $this->_test->assert(
+                                       new TrueExpectation(),
+                    count($this->_queue) == 0,
+                    sprintf($message, 'Should be no errors'));
         }
 
         /**
-         *    Global access to a single error queue.
-         *    @return        Global error queue object.
-         *    @access public
-         *    @static
+         *    @deprecated
          */
-        function &instance() {
-            static $queue = false;
-            if (! $queue) {
-                $queue = new SimpleErrorQueue();
+        function assertError($expected, $message) {
+            if (count($this->_queue) == 0) {
+                $this->_test->fail(sprintf($message, 'Expected error not found'));
+                return false;
             }
-            return $queue;
+            list($severity, $content, $file, $line) = $this->extract();
+            $severity = $this->getSeverityAsString($severity);
+            return $this->_test->assert(
+                    $expected,
+                    $content,
+                    sprintf($message, "Expected PHP error [$content] severity [$severity] in [$file] line [$line]"));
+        }
+
+        /**
+         *    Sets up an expectation of an error. If this is
+         *    not fulfilled at the end of the test, a failure
+         *    will occour. If the error does happen, then this
+         *    will cancel it out and send a pass message.
+         *    @param SimpleExpectation $expected    Expected error match.
+         *    @param string $message                Message to display.
+         *    @access public
+         */
+        function expectError($expected, $message) {
+            array_push(
+                    $this->_expectation_queue,
+                    array($expected, $message));
         }
 
         /**
-         *    Converst an error code into it's string
+         *    Converts an error code into it's string
          *    representation.
          *    @param $severity  PHP integer error code.
          *    @return           String version of error code.
      *    @static
      *    @access public
      */
-    function simpleTestErrorHandler($severity, $message, $filename, $line, $super_globals) {
+    function SimpleTestErrorHandler($severity, $message, $filename, $line, $super_globals) {
         if ($severity = $severity & error_reporting()) {
             restore_error_handler();
             if (ini_get('log_errors')) {
                 $label = SimpleErrorQueue::getSeverityAsString($severity);
                 error_log("$label: $message in $filename on line $line");
             }
-            $queue = &SimpleErrorQueue::instance();
-            $queue->add($severity, $message, $filename, $line, $super_globals);
-            set_error_handler('simpleTestErrorHandler');
+            $context = &SimpleTest::getContext();
+            $queue = &$context->get('SimpleErrorQueue');
+            $queue->add($severity, $message, $filename, $line);
+            set_error_handler('SimpleTestErrorHandler');
         }
     }
 ?>
\ No newline at end of file
index d8b14ef9323c3797f0af70bb9ae5fc892de53636..a2bf0afff07f7dfb36d2bcea6140ce6ce1ed5464 100644 (file)
      * for dependent libraries.
      */
     require_once(dirname(__FILE__) . '/invoker.php');
+    require_once(dirname(__FILE__) . '/expectation.php');
 
     /**
      *    Extension that traps exceptions and turns them into
-     *    an error message.
+     *    an error message. PHP5 only.
         *        @package SimpleTest
         *        @subpackage UnitTester
      */
         }
 
         /**
-         *    Invokes a test method and dispatches any
-         *    untrapped errors.
+         *    Invokes a test method whilst trapping expected
+         *    exceptions. Any left over unthrown exceptions
+         *    are then reported as failures.
          *    @param string $method    Test method to call.
-         *    @access public
          */
         function invoke($method) {
+            $trap = SimpleTest::getContext()->get('SimpleExceptionTrap');
+            $trap->clear();
             try {
                 parent::invoke($method);
             } catch (Exception $exception) {
-                $test_case = &$this->getTestCase();
-                $test_case->tearDown(); // Added by T.J.Hunt@open.ac.uk.
-                $test_case->exception($exception);
+                   if (! $trap->isExpected($this->getTestCase(), $exception)) {
+                       $this->getTestCase()->exception($exception);
+                   }
+                   $trap->clear();
+            }
+                       if ($message = $trap->getOutstanding()) {
+                               $this->getTestCase()->fail($message);
+                       }
+        }
+    }
+
+    /**
+     *    Tests exceptions either by type or the exact
+     *    exception. This could be improved to accept
+     *    a pattern expectation to test the error
+     *    message, but that will have to come later.
+        *        @package SimpleTest
+        *        @subpackage UnitTester
+     */
+    class ExceptionExpectation extends SimpleExpectation {
+        private $expected;
+
+        /**
+         *    Sets up the conditions to test against.
+         *    If the expected value is a string, then
+         *    it will act as a test of the class name.
+         *    An exception as the comparison will
+         *    trigger an identical match. Writing this
+         *    down now makes it look doubly dumb. I hope
+         *    come up with a better scheme later.
+         *    @param mixed $expected   A class name or an actual
+         *                             exception to compare with.
+         *    @param string $message   Message to display.
+         */
+        function __construct($expected, $message = '%s') {
+            $this->expected = $expected;
+            parent::__construct($message);
+        }
+
+        /**
+         *    Carry out the test.
+         *    @param Exception $compare    Value to check.
+         *    @return boolean              True if matched.
+         */
+        function test($compare) {
+            if (is_string($this->expected)) {
+                return ($compare instanceof $this->expected);
+            }
+            if (get_class($compare) != get_class($this->expected)) {
+                return false;
             }
+            return $compare->getMessage() == $this->expected->getMessage();
+        }
+
+        /**
+         *    Create the message to display describing the test.
+         *    @param Exception $compare     Exception to match.
+         *    @return string                Final message.
+         */
+        function testMessage($compare) {
+            if (is_string($this->expected)) {
+                return "Exception [" . $this->describeException($compare) .
+                        "] should be type [" . $this->expected . "]";
+            }
+            return "Exception [" . $this->describeException($compare) .
+                    "] should match [" .
+                    $this->describeException($this->expected) . "]";
+        }
+
+        /**
+         *    Summary of an Exception object.
+         *    @param Exception $compare     Exception to describe.
+         *    @return string                Text description.
+         */
+        protected function describeException($exception) {
+            return get_class($exception) . ": " . $exception->getMessage();
+        }
+    }
+
+    /**
+     *    Stores expected exceptions for when they
+     *    get thrown. Saves the irritating try...catch
+     *    block.
+        *        @package      SimpleTest
+        *        @subpackage   UnitTester
+     */
+    class SimpleExceptionTrap {
+        private $expected;
+        private $message;
+
+        /**
+         *    Clears down the queue ready for action.
+         */
+        function __construct() {
+            $this->clear();
+        }
+
+        /**
+         *    Sets up an expectation of an exception.
+         *    This has the effect of intercepting an
+         *    exception that matches.
+         *    @param SimpleExpectation $expected    Expected exception to match.
+         *    @param string $message                Message to display.
+         *    @access public
+         */
+        function expectException($expected = false, $message = '%s') {
+            if ($expected === false) {
+                $expected = new AnythingExpectation();
+            }
+            if (! SimpleExpectation::isExpectation($expected)) {
+                $expected = new ExceptionExpectation($expected);
+            }
+            $this->expected = $expected;
+            $this->message = $message;
+        }
+
+        /**
+         *    Compares the expected exception with any
+         *    in the queue. Issues a pass or fail and
+         *    returns the state of the test.
+         *    @param SimpleTestCase $test    Test case to send messages to.
+         *    @param Exception $exception    Exception to compare.
+         *    @return boolean                False on no match.
+         */
+        function isExpected($test, $exception) {
+            if ($this->expected) {
+                   return $test->assert($this->expected, $exception, $this->message);
+            }
+                       return false;
+        }
+
+               /**
+                *    Tests for any left over exception.
+                *    @return string/false     The failure message or false if none.
+                */
+               function getOutstanding() {
+                       return sprintf($this->message, 'Failed to trap exception');
+               }
+
+        /**
+         *    Discards the contents of the error queue.
+         */
+        function clear() {
+            $this->expected = false;
+            $this->message = false;
         }
     }
 ?>
\ No newline at end of file
index 7cb4cf820b81f2ecb48182982bb22d2e846dc115..a4e9de25f7a892dc36051a18d2690f0b47086779 100644 (file)
@@ -30,7 +30,6 @@
          *    @param string $message    Customised message on failure.
          */
         function SimpleExpectation($message = '%s') {
-            $this->_dumper = &new SimpleDumper();
             $this->_message = $message;
         }
         
         /**
          *    Overlays the generated message onto the stored user
          *    message. An additional message can be interjected.
-         *    @param mixed $compare      Comparison value.
-         *    @return string             Description of success
-         *                               or failure.
+         *    @param mixed $compare        Comparison value.
+         *    @param SimpleDumper $dumper  For formatting the results.
+         *    @return string               Description of success
+         *                                 or failure.
          *    @access public
          */
-        function overlayMessage($compare) {
+        function overlayMessage($compare, $dumper) {
+            $this->_dumper = $dumper;
             return sprintf($this->_message, $this->testMessage($compare));
         }
         
                     SimpleTestCompatibility::isA($expectation, 'SimpleExpectation');
         }
     }
+
+    /**
+     *    A wildcard expectation always matches.
+     *    @package SimpleTest
+     *    @subpackage MockObjects
+     */
+    class AnythingExpectation extends SimpleExpectation {
+
+        /**
+         *    Tests the expectation. Always true.
+         *    @param mixed $compare  Ignored.
+         *    @return boolean        True.
+         *    @access public
+         */
+        function test($compare) {
+            return true;
+        }
+
+        /**
+         *    Returns a human readable test message.
+         *    @param mixed $compare      Comparison value.
+         *    @return string             Description of success
+         *                               or failure.
+         *    @access public
+         */
+        function testMessage($compare) {
+            $dumper = &$this->_getDumper();
+            return 'Anything always matches [' . $dumper->describeValue($compare) . ']';
+        }
+    }
+
+    /**
+     *    An expectation that passes on boolean true.
+     *    @package SimpleTest
+     *    @subpackage MockObjects
+     */
+    class TrueExpectation extends SimpleExpectation {
+
+        /**
+         *    Tests the expectation.
+         *    @param mixed $compare  Should be true.
+         *    @return boolean        True on match.
+         *    @access public
+         */
+        function test($compare) {
+            return (boolean)$compare;
+        }
+
+        /**
+         *    Returns a human readable test message.
+         *    @param mixed $compare      Comparison value.
+         *    @return string             Description of success
+         *                               or failure.
+         *    @access public
+         */
+        function testMessage($compare) {
+            $dumper = &$this->_getDumper();
+            return 'Expected true, got [' . $dumper->describeValue($compare) . ']';
+        }
+    }
+    
+    /**
+     *    An expectation that passes on boolean false.
+     *    @package SimpleTest
+     *    @subpackage MockObjects
+     */
+    class FalseExpectation extends SimpleExpectation {
+
+        /**
+         *    Tests the expectation.
+         *    @param mixed $compare  Should be false.
+         *    @return boolean        True on match.
+         *    @access public
+         */
+        function test($compare) {
+            return ! (boolean)$compare;
+        }
+
+        /**
+         *    Returns a human readable test message.
+         *    @param mixed $compare      Comparison value.
+         *    @return string             Description of success
+         *                               or failure.
+         *    @access public
+         */
+        function testMessage($compare) {
+            $dumper = &$this->_getDumper();
+            return 'Expected false, got [' . $dumper->describeValue($compare) . ']';
+        }
+    }
     
     /**
      *    Test for equality.
         /**
          *    Describes a pattern match including the string
          *    found and it's position.
-     *    @package SimpleTest
-     *    @subpackage UnitTester
+         *    @package SimpleTest
+         *    @subpackage UnitTester
          *    @param string $pattern        Regex to match against.
          *    @param string $subject        Subject to search.
          *    @access protected
         function _describePatternMatch($pattern, $subject) {
             preg_match($pattern, $subject, $matches);
             $position = strpos($subject, $matches[0]);
-            $dumper = &$this->_getDumper();
+            $dumper = $this->_getDumper();
             return "Pattern [$pattern] detected at character [$position] in [" .
                     $dumper->describeValue($subject) . "] as [" .
                     $matches[0] . "] in region [" .
                     "] should contain method [$method]";
         }
     }
-?>
+?>
\ No newline at end of file
index 7c91fb0043591bda20ba102692995c1239d8cb93..4f9f4ad3eeb6d3bce0f1b984bcfe7e5c80d7cc9a 100644 (file)
@@ -58,7 +58,7 @@
          *    @public
          */
         function assertNotNull($value, $message = "%s") {
-            parent::assertTrue(isset($value), $message);
+            parent::assert(new TrueExpectation(), isset($value), $message);
         }
         
         /**
@@ -68,7 +68,7 @@
          *    @public
          */
         function assertNull($value, $message = "%s") {
-            parent::assertTrue(!isset($value), $message);
+            parent::assert(new TrueExpectation(), !isset($value), $message);
         }
         
         /**
@@ -86,7 +86,8 @@
                     "[" . $dumper->describeValue($first) .
                             "] and [" . $dumper->describeValue($second) .
                             "] should reference the same object");
-            return $this->assertTrue(
+            return $this->assert(
+                                       new TrueExpectation(),
                     SimpleTestCompatibility::isReference($first, $second),
                     $message);
         }
                     "[" . $dumper->describeValue($first) .
                             "] and [" . $dumper->describeValue($second) .
                             "] should not be the same object");
-            return $this->assertFalse(
+            return $this->assert(
+                                       new falseExpectation(),
                     SimpleTestCompatibility::isReference($first, $second),
                     $message);
         }
          *    @public
          */
         function assertTrue($condition, $message = "%s") {
-            parent::assertTrue($condition, $message);
+            parent::assert(new TrueExpectation(), $condition, $message);
         }
         
         /**
          *    @public
          */
         function assertFalse($condition, $message = "%s") {
-            parent::assertTrue(!$condition, $message);
+            parent::assert(new FalseExpectation(), $condition, $message);
         }
         
         /**
          *    @public
          */
         function assertType($value, $type, $message = "%s") {
-            parent::assertTrue(gettype($value) == strtolower($type), $message);
+            parent::assert(new TrueExpectation(), gettype($value) == strtolower($type), $message);
         }
         
         /**
index 7646b9c35c3e77e14be4d1a07f56fe90cccca855..1628ed7d231a798cb32b586a7c6e1ec2e2108604 100644 (file)
@@ -38,7 +38,7 @@
          *    @public
          */
         function assert($condition, $message = false) {
-            parent::assertTrue($condition, $message);
+            parent::assert(new TrueExpectation(), $condition, $message);
         }
         
         /**
index ccb16df678a08cbac476b32f44a34b9cd45f7d0d..aefdd31e6dd38c39364efe2e38bd2c6d333810fb 100644 (file)
         define('MOCK_ANYTHING', '*');
     }
 
-    /**
-     *    A wildcard expectation always matches.
-        *    @package SimpleTest
-        *    @subpackage MockObjects
-     */
-    class AnythingExpectation extends SimpleExpectation {
-
-        /**
-         *    Tests the expectation. Always true.
-         *    @param mixed $compare  Ignored.
-         *    @return boolean        True.
-         *    @access public
-         */
-        function test($compare) {
-            return true;
-        }
-
-        /**
-         *    Returns a human readable test message.
-         *    @param mixed $compare      Comparison value.
-         *    @return string             Description of success
-         *                               or failure.
-         *    @access public
-         */
-        function testMessage($compare) {
-            $dumper = &$this->_getDumper();
-            return 'Anything always matches [' . $dumper->describeValue($compare) . ']';
-        }
-    }
-
     /**
      *    Parameter comparison assertion.
         *    @package SimpleTest
@@ -70,8 +40,6 @@
          *                              those that are wildcarded.
          *                              If the value is not an array
          *                              then it is considered to match any.
-         *    @param mixed $wildcard    Any parameter matching this
-         *                              will always match.
          *    @param string $message    Customised message on failure.
          *    @access public
          */
                 $comparison = $this->_coerceToExpectation($expected[$i]);
                 if (! $comparison->test($parameters[$i])) {
                     $messages[] = "parameter " . ($i + 1) . " with [" .
-                            $comparison->overlayMessage($parameters[$i]) . "]";
+                            $comparison->overlayMessage($parameters[$i], $this->_getDumper()) . "]";
                 }
             }
             return "Parameter expectation differs at " . implode(" and ", $messages);
          *    @access protected
          */
         function &_getCurrentTestCase() {
-            return SimpleTest::getCurrent();
+            $context = &SimpleTest::getContext();
+            return $context->getTest();
         }
 
         /**
          *    test method has finished. Totals up the call
          *    counts and triggers a test assertion if a test
          *    is present for expected call counts.
-         *    @param string $method    Current method name.
+         *    @param string $test_method    Current method name.
+         *    @param SimpleTestCase $test   Test to send message to.
          *    @access public
          */
-        function atTestEnd($method) {
+        function atTestEnd($test_method, &$test) {
             foreach ($this->_expected_counts as $method => $expectation) {
-                $this->_assertTrue(
-                        $expectation->test($this->getCallCount($method)),
-                        $expectation->overlayMessage($this->getCallCount($method)));
+                $test->assert($expectation, $this->getCallCount($method));
             }
             foreach ($this->_max_counts as $method => $expectation) {
                 if ($expectation->test($this->getCallCount($method))) {
-                    $this->_assertTrue(
-                            true,
-                            $expectation->overlayMessage($this->getCallCount($method)));
+                    $test->assert($expectation, $this->getCallCount($method));
                 }
             }
         }
          *    @access private
          */
         function _checkExpectations($method, $args, $timing) {
+            $test = &$this->_getCurrentTestCase();
             if (isset($this->_max_counts[$method])) {
                 if (! $this->_max_counts[$method]->test($timing + 1)) {
-                    $this->_assertTrue(
-                            false,
-                            $this->_max_counts[$method]->overlayMessage($timing + 1));
+                    $test->assert($this->_max_counts[$method], $timing + 1);
                 }
             }
             if (isset($this->_expected_args_at[$timing][$method])) {
-                $this->_assertTrue(
-                        $this->_expected_args_at[$timing][$method]->test($args),
-                        "Mock method [$method] at [$timing] -> " .
-                                $this->_expected_args_at[$timing][$method]->overlayMessage($args));
+                $test->assert(
+                        $this->_expected_args_at[$timing][$method],
+                        $args,
+                        "Mock method [$method] at [$timing] -> %s");
             } elseif (isset($this->_expected_args[$method])) {
-                $this->_assertTrue(
-                        $this->_expected_args[$method]->test($args),
-                        "Mock method [$method] -> " . $this->_expected_args[$method]->overlayMessage($args));
+                $test->assert(
+                        $this->_expected_args[$method],
+                        $args,
+                        "Mock method [$method] -> %s");
             }
         }
-
-        /**
-         *    Triggers an assertion on the held test case.
-         *    Should be overridden when using another test
-         *    framework other than the SimpleTest one if the
-         *    assertion method has a different name.
-         *    @param boolean $assertion     True will pass.
-         *    @param string $message        Message that will go with
-         *                                  the test event.
-         *    @access protected
-         */
-        function _assertTrue($assertion, $message) {
-            $test = &$this->_getCurrentTestCase();
-            $test->assertTrue($assertion, $message);
-        }
     }
 
     /**
          *    @access public
          */
         function Mock() {
-            trigger_error('Mock factory methods are class only.');
+            trigger_error('Mock factory methods are static.');
         }
 
         /**
 
         /**
          *    Uses a stack trace to find the line of an assertion.
-         *    @param array $stack      Stack frames top most first. Only
-         *                             needed if not using the PHP
-         *                             backtrace function.
-         *    @return string           Location of first expect*
-         *                             method embedded in format string.
          *    @access public
          *    @static
          */
-        function getExpectationLine($stack = false) {
-            if ($stack === false) {
-                $stack = SimpleTestCompatibility::getStackTrace();
-            }
-            return SimpleDumper::getFormattedAssertionLine($stack);
+        function getExpectationLine() {
+            $trace = new SimpleStackTrace(array('expect'));
+            return $trace->traceMethod();
         }
     }
 
             }
             $mock_reflection = new SimpleReflection($this->_mock_class);
             if ($mock_reflection->classExistsSansAutoload()) {
-                trigger_error("Partial mock class [$mock_class] already exists");
+                trigger_error('Partial mock class [' . $this->_mock_class . '] already exists');
                 return false;
             }
             return eval($this->_extendClassCode($methods));
index 97db5ca1c4f8f1d98d2794b59d6289d1baf2fd8c..06977f8c21a60cabd78a260055d3c83f3929aa1f 100644 (file)
          *    @access public
          */
         function acceptAttributeToken($token, $event) {
-            if ($event == LEXER_UNMATCHED) {
-                $this->_attributes[$this->_current_attribute] .=
-                        SimpleHtmlSaxParser::decodeHtml($token);
-            }
-            if ($event == LEXER_SPECIAL) {
-                $this->_attributes[$this->_current_attribute] .=
-                        preg_replace('/^=\s*/' , '', SimpleHtmlSaxParser::decodeHtml($token));
+            if ($this->_current_attribute) {
+                if ($event == LEXER_UNMATCHED) {
+                    $this->_attributes[$this->_current_attribute] .=
+                            SimpleHtmlSaxParser::decodeHtml($token);
+                }
+                if ($event == LEXER_SPECIAL) {
+                    $this->_attributes[$this->_current_attribute] .=
+                            preg_replace('/^=\s*/' , '', SimpleHtmlSaxParser::decodeHtml($token));
+                }
             }
             return true;
         }
index b816ef34fa2623f25cd7540c00b110f2a7d6588b..7e023f37d098738e9b226b344523022e96c0855b 100644 (file)
@@ -8,8 +8,8 @@
 
     /**
      *    Version specific reflection API.
-        *    @package SimpleTest
-        *    @subpackage UnitTester
+     *    @package SimpleTest
+     *    @subpackage UnitTester
      */
     class SimpleReflection {
         var $_interface;
          *    @access public
          */
         function getSignature($name) {
-               if ($name == '__get') {
-                       return 'function __get($key)';
-               }
                if ($name == '__set') {
                        return 'function __set($key, $value)';
                }
+               if ($name == '__call') {
+                       return 'function __call($method, $arguments)';
+               }
+            if (version_compare(phpversion(), '5.1.0', '>=')) {
+                if (in_array($name, array('__get', '__isset', $name == '__unset'))) {
+                    return "function {$name}(\$key)";
+                }
+            }
                if (! is_callable(array($this->_interface, $name))) {
                        return "function $name()";
                }
          *    @access private
          */
         function _getFullSignature($name) {
-               $interface = new ReflectionClass($this->_interface);
-               $method = $interface->getMethod($name);
-               $reference = $method->returnsReference() ? '&' : '';
-               return "function $reference$name(" .
-                       implode(', ', $this->_getParameterSignatures($method)) .
-                       ")";
+            $interface = new ReflectionClass($this->_interface);
+            $method = $interface->getMethod($name);
+            $reference = $method->returnsReference() ? '&' : '';
+            return "function $reference$name(" .
+                    implode(', ', $this->_getParameterSignatures($method)) .
+                    ")";
         }
 
         /**
          *    Gets the source code for each parameter.
          *    @param ReflectionMethod $method   Method object from
-         *                                                                             reflection API
+         *                                                         reflection API
          *    @return array                     List of strings, each
          *                                      a snippet of code.
          *    @access private
          */
         function _getParameterSignatures($method) {
-               $signatures = array();
+            $signatures = array();
             foreach ($method->getParameters() as $parameter) {
                 $type = $parameter->getClass();
                $signatures[] =
-                                       (! is_null($type) ? $type->getName() . ' ' : '') .
-                               ($parameter->isPassedByReference() ? '&' : '') .
-                               '$' . $this->_suppressSpurious($parameter->getName()) .
-                               ($this->_isOptional($parameter) ? ' = null' : '');
+                        (! is_null($type) ? $type->getName() . ' ' : '') .
+                        ($parameter->isPassedByReference() ? '&' : '') .
+                        '$' . $this->_suppressSpurious($parameter->getName()) .
+                        ($this->_isOptional($parameter) ? ' = null' : '');
             }
             return $signatures;
         }
index cb6b9f2d75a1da579f1eaf50b674dc7642df8e22..c7e02b124ee7ce518ae3a4245f61e429d5ba1547 100644 (file)
@@ -75,7 +75,8 @@
          *    @access protected
          */
         function &_createBrowser() {
-            return new SimpleBrowser();
+            $browser = &new SimpleBrowser();
+            return $browser;
         }
         
         /**
@@ -85,7 +86,8 @@
          *    @access protected
          */
         function &_createParser(&$reporter) {
-            return new SimpleTestXmlParser($reporter);
+            $parser = &new SimpleTestXmlParser($reporter);
+            return $parser;
         }
         
         /**
index 3be2a1572b658ef84d3424724bc31e3812670897..492faab9af83576d1123c32c1cbf485e548ec71b 100644 (file)
@@ -40,6 +40,7 @@
          */
         function paintHeader($test_name) {
             $this->sendNoCacheHeaders();
+            print "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">";
             print "<html>\n<head>\n<title>$test_name</title>\n";
             print "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=" .
                     $this->_character_set . "\">\n";
@@ -74,7 +75,9 @@
          *    @access protected
          */
         function _getCss() {
-            return ".fail { color: red; } pre { background-color: lightgray; }";
+            return ".fail { background-color: inherit; color: red; }" .
+                    ".pass { background-color: inherit; color: green; }" .
+                    " pre { background-color: lightgray; color: inherit; }";
         }
 
         /**
         }
 
         /**
-         *    Paints a PHP error or exception.
+         *    Paints a PHP error.
          *    @param string $message        Message is ignored.
          *    @access public
-         *    @abstract
          */
         function paintError($message) {
             parent::paintError($message);
             print " -&gt; <strong>" . $this->_htmlEntities($message) . "</strong><br />\n";
         }
 
+        /**
+         *    Paints a PHP exception.
+         *    @param Exception $exception        Exception to display.
+         *    @access public
+         */
+        function paintException($exception) {
+            parent::paintException($exception);
+            print "<span class=\"fail\">Exception</span>: ";
+            $breadcrumb = $this->getTestList();
+            array_shift($breadcrumb);
+            print implode(" -&gt; ", $breadcrumb);
+            $message = 'Unexpected exception of type [' . get_class($exception) .
+                    '] with message ['. $exception->getMessage() .
+                    '] in ['. $exception->getFile() .
+                    ' line ' . $exception->getLine() . ']';
+            print " -&gt; <strong>" . $this->_htmlEntities($message) . "</strong><br />\n";
+        }
+               
+               /**
+                *    Prints the message for skipping tests.
+         *    @param string $message    Text of skip condition.
+         *    @access public
+         */
+               function paintSkip($message) {
+            parent::paintSkip($message);
+            print "<span class=\"pass\">Skipped</span>: ";
+            $breadcrumb = $this->getTestList();
+            array_shift($breadcrumb);
+            print implode(" -&gt; ", $breadcrumb);
+            print " -&gt; " . $this->_htmlEntities($message) . "<br />\n";
+               }
+
         /**
          *    Paints formatted text such as dumped variables.
          *    @param string $message        Text to show.
 
         /**
          *    Paints a PHP error or exception.
-         *    @param string $message        Message is ignored.
+         *    @param string $message        Message to be shown.
          *    @access public
          *    @abstract
          */
             print "Exception " . $this->getExceptionCount() . "!\n$message\n";
         }
 
+        /**
+         *    Paints a PHP error or exception.
+         *    @param Exception $exception      Exception to describe.
+         *    @access public
+         *    @abstract
+         */
+        function paintException($exception) {
+            parent::paintException($exception);
+            $message = 'Unexpected exception of type [' . get_class($exception) .
+                    '] with message ['. $exception->getMessage() .
+                    '] in ['. $exception->getFile() .
+                    ' line ' . $exception->getLine() . ']';
+            print "Exception " . $this->getExceptionCount() . "!\n$message\n";
+        }
+               
+               /**
+                *    Prints the message for skipping tests.
+         *    @param string $message    Text of skip condition.
+         *    @access public
+         */
+               function paintSkip($message) {
+            parent::paintSkip($message);
+            print "Skip: $message\n";
+               }
+
         /**
          *    Paints formatted text such as dumped variables.
          *    @param string $message        Text to show.
index 62d232d6520437330f0ee243bcfb09a634d75f0d..279d714dbfbee4ea1683f2157fa7bb01d999bba1 100644 (file)
         }
 
         /**
-         *    Deals with PHP 4 throwing an error or PHP 5
-         *    throwing an exception.
+         *    Deals with PHP 4 throwing an error.
          *    @param string $message    Text of error formatted by
          *                              the test case.
          *    @access public
             $this->_exceptions++;
         }
 
+        /**
+         *    Deals with PHP 5 throwing an exception.
+         *    @param Exception $exception    The actual exception thrown.
+         *    @access public
+         */
+        function paintException($exception) {
+            $this->_exceptions++;
+        }
+               
+               /**
+                *    Prints the message for skipping tests.
+         *    @param string $message    Text of skip condition.
+         *    @access public
+         */
+               function paintSkip($message) {
+               }
+
         /**
          *    Accessor for the number of passes so far.
          *    @return integer       Number of passes.
             $this->_size = null;
             $this->_progress = 0;
         }
+        
+        /**
+         *    Gets the formatter for variables and other small
+         *    generic data items.
+         *    @return SimpleDumper          Formatter.
+         *    @access public
+         */
+        function getDumper() {
+            return new SimpleDumper();
+        }
 
         /**
          *    Paints the start of a group test. Will also paint
         var $_reporter;
 
         /**
-         *    Mediates between teh reporter and the test case.
+         *    Mediates between the reporter and the test case.
          *    @param SimpleScorer $reporter       Reporter to receive events.
          */
         function SimpleReporterDecorator(&$reporter) {
         function &createInvoker(&$invoker) {
             return $this->_reporter->createInvoker($invoker);
         }
+        
+        /**
+         *    Gets the formatter for variables and other small
+         *    generic data items.
+         *    @return SimpleDumper          Formatter.
+         *    @access public
+         */
+        function getDumper() {
+            return $this->_reporter->getDumper();
+        }
 
         /**
          *    Paints the start of a group test.
             $this->_reporter->paintError($message);
         }
 
+        /**
+         *    Chains to the wrapped reporter.
+         *    @param Exception $exception        Exception to show.
+         *    @access public
+         */
+        function paintException($exception) {
+            $this->_reporter->paintException($exception);
+        }
+               
+               /**
+                *    Prints the message for skipping tests.
+         *    @param string $message    Text of skip condition.
+         *    @access public
+         */
+               function paintSkip($message) {
+            $this->_reporter->paintSkip($message);
+               }
+
         /**
          *    Chains to the wrapped reporter.
          *    @param string $message        Text to display.
             }
             return $invoker;
         }
+        
+        /**
+         *    Gets the formatter for variables and other small
+         *    generic data items.
+         *    @return SimpleDumper          Formatter.
+         *    @access public
+         */
+        function getDumper() {
+            return new SimpleDumper();
+        }
 
         /**
          *    Paints the start of a group test.
                 $this->_reporters[$i]->paintError($message);
             }
         }
+               
+        /**
+         *    Chains to the wrapped reporter.
+         *    @param Exception $exception    Exception to display.
+         *    @access public
+         */
+        function paintException($exception) {
+            for ($i = 0; $i < count($this->_reporters); $i++) {
+                $this->_reporters[$i]->paintException($exception);
+            }
+        }
+
+               /**
+                *    Prints the message for skipping tests.
+         *    @param string $message    Text of skip condition.
+         *    @access public
+         */
+               function paintSkip($message) {
+            for ($i = 0; $i < count($this->_reporters); $i++) {
+                $this->_reporters[$i]->paintSkip($message);
+            }
+               }
 
         /**
          *    Chains to the wrapped reporter.
index 1e513ef184b8105ed1982dc4caa46012282cb2e2..d47d2d6dce912d7755fb9b701a8aa780cdb8af69 100644 (file)
@@ -63,7 +63,7 @@
 
     /**
      *    Test case for testing of command line scripts and
-     *    utilities. Usually scripts taht are external to the
+     *    utilities. Usually scripts that are external to the
      *    PHP code, but support it in some way.
         *        @package SimpleTest
         *        @subpackage UnitTester
             $shell = &$this->_getShell();
             return $shell->getOutputAsList();
                }
+
+        /**
+         *    Called from within the test methods to register
+         *    passes and failures.
+         *    @param boolean $result    Pass on true.
+         *    @param string $message    Message to display describing
+         *                              the test state.
+         *    @return boolean           True on pass
+         *    @access public
+         */
+        function assertTrue($result, $message = false) {
+            return $this->assert(new TrueExpectation(), $result, $message);
+        }
+
+        /**
+         *    Will be true on false and vice versa. False
+         *    is the PHP definition of false, so that null,
+         *    empty strings, zero and an empty array all count
+         *    as false.
+         *    @param boolean $result    Pass on false.
+         *    @param string $message    Message to display.
+         *    @return boolean           True on pass
+         *    @access public
+         */
+        function assertFalse($result, $message = '%s') {
+            return $this->assert(new FalseExpectation(), $result, $message);
+        }
         
         /**
          *    Will trigger a pass if the two parameters have
             return $shell;
         }
     }
-?>
\ No newline at end of file
+?>
index 0a1693a65c212167dc49aa899603076b7fbd2fb9..6464bdc36be3cc5069debe2d49cf2bc19cd093dd 100644 (file)
     /**#@-*/
 
     /**
-     *    Static global directives and options. I hate this
-     *    class. It's a mixture of reference hacks, configuration
-     *    and previous design screw-ups that I have to maintain
-     *    to keep backward compatibility.
+     *    Registry and test context. Includes a few
+     *    global options that I'm slowly getting rid of.
      *   @package      SimpleTest
      */
     class SimpleTest {
@@ -59,8 +57,8 @@
          *    missing abstract declarations. This cannot
          *    be done whilst loading classes wiithout forcing
          *    a particular order on the class declarations and
-         *    the ignore() calls. It's nice to havethe ignore()
-         *    calls at the top of teh file.
+         *    the ignore() calls. It's just nice to have the ignore()
+         *    calls at the top of the file before the actual declarations.
          *    @param array $classes     Class names of interest.
          *    @static
          *    @access public
             return $registry['DefaultProxyPassword'];
         }
 
-        /**
-         *    Sets the current test case instance. This
-         *    global instance can be used by the mock objects
-         *    to send message to the test cases.
-         *    @param SimpleTestCase $test        Test case to register.
-         *    @access public
-         *    @static
-         */
-        function setCurrent(&$test) {
-            $registry = &SimpleTest::_getRegistry();
-            $registry['CurrentTestCase'] = &$test;
-        }
-
-        /**
-         *    Accessor for current test instance.
-         *    @return SimpleTEstCase        Currently running test.
-         *    @access public
-         *    @static
-         */
-        function &getCurrent() {
-            $registry = &SimpleTest::_getRegistry();
-            return $registry['CurrentTestCase'];
-        }
-
         /**
          *    Accessor for global registry of options.
          *    @return hash           All stored values.
             return $registry;
         }
 
+        /**
+         *    Accessor for the context of the current
+         *    test run.
+         *    @return SimpleTestContext    Current test run.
+         *    @access public
+         *    @static
+         */
+        function &getContext() {
+            static $context = false;
+            if (! $context) {
+                $context = new SimpleTestContext();
+            }
+            return $context;
+        }
+
         /**
          *    Constant default values.
          *    @return hash       All registry defaults.
         }
     }
 
+    /**
+     *    Container for all components for a specific
+     *    test run. Makes things like error queues
+     *    available to PHP event handlers, and also
+     *    gets around some nasty reference issues in
+     *    the mocks.
+     *   @package      SimpleTest
+     */
+    class SimpleTestContext {
+        var $_test;
+        var $_reporter;
+        var $_resources;
+
+        /**
+         *    Clears down the current context.
+         *    @access public
+         */
+        function clear() {
+            $this->_resources = array();
+        }
+
+        /**
+         *    Sets the current test case instance. This
+         *    global instance can be used by the mock objects
+         *    to send message to the test cases.
+         *    @param SimpleTestCase $test        Test case to register.
+         *    @access public
+         */
+        function setTest(&$test) {
+            $this->clear();
+            $this->_test = &$test;
+        }
+
+        /**
+         *    Accessor for currently running test case.
+         *    @return SimpleTestCase    Current test.
+         *    @acess pubic
+         */
+        function &getTest() {
+            return $this->_test;
+        }
+
+        /**
+         *    Sets the current reporter. This
+         *    global instance can be used by the mock objects
+         *    to send messages.
+         *    @param SimpleReporter $reporter     Reporter to register.
+         *    @access public
+         */
+        function setReporter(&$reporter) {
+            $this->clear();
+            $this->_reporter = &$reporter;
+        }
+
+        /**
+         *    Accessor for current reporter.
+         *    @return SimpleReporter    Current reporter.
+         *    @acess pubic
+         */
+        function &getReporter() {
+            return $this->_reporter;
+        }
+
+        /**
+         *    Accessor for the Singleton resource.
+         *    @return object       Global resource.
+         *    @access public
+         *    @static
+         */
+        function &get($resource) {
+            if (! isset($this->_resources[$resource])) {
+                $this->_resources[$resource] = &new $resource();
+            }
+            return $this->_resources[$resource];
+        }
+    }
+
+    /**
+     *    Interrogates the stack trace to recover the
+     *    failure point.
+        *        @package SimpleTest
+        *        @subpackage UnitTester
+     */
+    class SimpleStackTrace {
+        var $_prefixes;
+
+        /**
+         *    Stashes the list of target prefixes.
+         *    @param array $prefixes      List of method prefixes
+         *                                to search for.
+         */
+        function SimpleStackTrace($prefixes) {
+            $this->_prefixes = $prefixes;
+        }
+
+        /**
+         *    Extracts the last method name that was not within
+         *    Simpletest itself. Captures a stack trace if none given.
+         *    @param array $stack      List of stack frames.
+         *    @return string           Snippet of test report with line
+         *                             number and file.
+         *    @access public
+         */
+        function traceMethod($stack = false) {
+            $stack = $stack ? $stack : $this->_captureTrace();
+            foreach ($stack as $frame) {
+                if ($this->_frameLiesWithinSimpleTestFolder($frame)) {
+                    continue;
+                }
+                if ($this->_frameMatchesPrefix($frame)) {
+                    return ' at [' . $frame['file'] . ' line ' . $frame['line'] . ']';
+                }
+            }
+            return '';
+        }
+
+        /**
+         *    Test to see if error is generated by SimpleTest itself.
+         *    @param array $frame     PHP stack frame.
+         *    @return boolean         True if a SimpleTest file.
+         *    @access private
+         */
+        function _frameLiesWithinSimpleTestFolder($frame) {
+            if (isset($frame['file'])) {
+                $path = substr(SIMPLE_TEST, 0, -1);
+                if (strpos($frame['file'], $path) === 0) {
+                    if (dirname($frame['file']) == $path) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+
+        /**
+         *    Tries to determine if the method call is an assert, etc.
+         *    @param array $frame     PHP stack frame.
+         *    @return boolean         True if matches a target.
+         *    @access private
+         */
+        function _frameMatchesPrefix($frame) {
+            foreach ($this->_prefixes as $prefix) {
+                if (strncmp($frame['function'], $prefix, strlen($prefix)) == 0) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        /**
+         *    Grabs a current stack trace.
+         *    @return array        Fulle trace.
+         *    @access private
+         */
+        function _captureTrace() {
+            if (function_exists('debug_backtrace')) {
+                return array_reverse(debug_backtrace());
+            }
+            return array();
+        }
+    }
+
     /**
      *    @deprecated
      */
             return Simpletest::getDefaultProxyPassword();
         }
     }
-?>
\ No newline at end of file
+?>
index c2f56f009ea123e6c1ca3e84ca4581f5aa538eea..62d03493b4e99ab84315e006c58dbc35e8640d62 100644 (file)
         require_once(dirname(__FILE__) . '/reflection_php4.php');
     }
     if (! defined('SIMPLE_TEST')) {
-        /**
-         * @ignore
-         */
-        define('SIMPLE_TEST', dirname(__FILE__) . '/');
+        /** @ignore */
+        define('SIMPLE_TEST', dirname(__FILE__) . DIRECTORY_SEPARATOR);
     }
     /**#@-*/
 
@@ -43,6 +41,7 @@
         var $_label = false;
         var $_reporter;
         var $_observers;
+               var $_should_skip = false;
 
         /**
          *    Sets up the test with no display.
             return $this->_label ? $this->_label : get_class($this);
         }
 
+        /**
+         *    This is a placeholder for skipping tests. In this
+         *    method you place skipIf() and skipUnless() calls to
+         *    set the skipping state.
+         *    @access public
+         */
+        function skip() {
+        }
+               
+        /**
+         *    Will issue a message to the reporter and tell the test
+         *    case to skip if the incoming flag is true.
+         *    @param string $should_skip    Condition causing the tests to be skipped.
+         *    @param string $message           Text of skip condition.
+         *    @access public
+         */
+        function skipIf($should_skip, $message = '%s') {
+                       if ($should_skip && ! $this->_should_skip) {
+                               $this->_should_skip = true;
+                               $message = sprintf($message, 'Skipping [' . get_class($this) . ']');
+                               $this->_reporter->paintSkip($message . $this->getAssertionLine());
+                       }
+        }
+               
+        /**
+         *    Will issue a message to the reporter and tell the test
+         *    case to skip if the incoming flag is false.
+         *    @param string $shouldnt_skip  Condition causing the tests to be run.
+         *    @param string $message           Text of skip condition.
+         *    @access public
+         */
+        function skipUnless($shouldnt_skip, $message = false) {
+                       $this->skipIf(! $shouldnt_skip, $message);
+        }
+               
         /**
          *    Used to invoke the single tests.
          *    @return SimpleInvoker        Individual test runner.
          *    starting with the string "test" unless a method
          *    is specified.
          *    @param SimpleReporter $reporter    Current test reporter.
+         *    @return boolean                    True if all tests passed.
          *    @access public
          */
         function run(&$reporter) {
-            SimpleTest::setCurrent($this);
+                       $context = &SimpleTest::getContext();
+                       $context->setTest($this);
+                       $context->setReporter($reporter);
             $this->_reporter = &$reporter;
-            $this->_reporter->paintCaseStart($this->getLabel());
-            foreach ($this->getTests() as $method) {
-                if ($this->_reporter->shouldInvoke($this->getLabel(), $method)) {
-                    $invoker = &$this->_reporter->createInvoker($this->createInvoker());
-                    $invoker->before($method);
-                    $invoker->invoke($method);
-                    $invoker->after($method);
+            $reporter->paintCaseStart($this->getLabel());
+                       $this->skip();
+            if (! $this->_should_skip) {
+                foreach ($this->getTests() as $method) {
+                    if ($reporter->shouldInvoke($this->getLabel(), $method)) {
+                        $invoker = &$this->_reporter->createInvoker($this->createInvoker());
+                        $invoker->before($method);
+                        $invoker->invoke($method);
+                        $invoker->after($method);
+                    }
                 }
             }
-            $this->_reporter->paintCaseEnd($this->getLabel());
+            $reporter->paintCaseEnd($this->getLabel());
             unset($this->_reporter);
             return $reporter->getStatus();
         }
          */
         function after($method) {
             for ($i = 0; $i < count($this->_observers); $i++) {
-                $this->_observers[$i]->atTestEnd($method);
+                $this->_observers[$i]->atTestEnd($method, $this);
             }
             $this->_reporter->paintMethodEnd($method);
         }
         }
 
         /**
-         *    Sends a pass event with a message.
-         *    @param string $message        Message to send.
-         *    @access public
+         *    @deprecated
          */
         function pass($message = "Pass") {
             if (! isset($this->_reporter)) {
                 trigger_error('Can only make assertions within test methods');
             }
             $this->_reporter->paintError(
-                    "Unexpected PHP error [$message] severity [$severity] in [$file] line [$line]");
+                    "Unexpected PHP error [$message] severity [$severity] in [$file line $line]");
         }
 
         /**
          *    @access public
          */
         function exception($exception) {
-            $this->_reporter->paintError(
-                    'Unexpected exception of type [' . get_class($exception) .
-                    '] with message ['. $exception->getMessage() .
-                    '] in ['. $exception->getFile() .
-                    '] line [' . $exception->getLine() . ']');
+            $this->_reporter->paintException($exception);
         }
 
         /**
-         *    Sends a user defined event to the test reporter.
-         *    This is for small scale extension where
-         *    both the test case and either the reporter or
-         *    display are subclassed.
-         *    @param string $type       Type of event.
-         *    @param mixed $payload     Object or message to deliver.
-         *    @access public
+         *    @deprecated
          */
         function signal($type, &$payload) {
             if (! isset($this->_reporter)) {
             $this->_reporter->paintSignal($type, $payload);
         }
 
-        /**
-         *    Cancels any outstanding errors.
-         *    @access public
-         */
-        function swallowErrors() {
-            $queue = &SimpleErrorQueue::instance();
-            $queue->clear();
-        }
-
         /**
          *    Runs an expectation directly, for extending the
          *    tests with new expectation classes.
          *    @access public
          */
         function assert(&$expectation, $compare, $message = '%s') {
-            return $this->assertTrue(
-                    $expectation->test($compare),
-                    sprintf($message, $expectation->overlayMessage($compare)));
+            if ($expectation->test($compare)) {
+                return $this->pass(sprintf(
+                        $message,
+                        $expectation->overlayMessage($compare, $this->_reporter->getDumper())));
+            } else {
+                return $this->fail(sprintf(
+                        $message,
+                        $expectation->overlayMessage($compare, $this->_reporter->getDumper())));
+            }
         }
 
         /**
                return $this->assert($expectation, $compare, $message);
         }
 
-        /**
-         *    Called from within the test methods to register
-         *    passes and failures.
-         *    @param boolean $result    Pass on true.
-         *    @param string $message    Message to display describing
-         *                              the test state.
-         *    @return boolean           True on pass
-         *    @access public
-         */
-        function assertTrue($result, $message = false) {
-            if (! $message) {
-                $message = 'True assertion got ' . ($result ? 'True' : 'False');
-            }
-            if ($result) {
-                return $this->pass($message);
-            } else {
-                return $this->fail($message);
-            }
-        }
-
-        /**
-         *    Will be true on false and vice versa. False
-         *    is the PHP definition of false, so that null,
-         *    empty strings, zero and an empty array all count
-         *    as false.
-         *    @param boolean $result    Pass on false.
-         *    @param string $message    Message to display.
-         *    @return boolean           True on pass
-         *    @access public
-         */
-        function assertFalse($result, $message = false) {
-            if (! $message) {
-                $message = 'False assertion got ' . ($result ? 'True' : 'False');
-            }
-            return $this->assertTrue(! $result, $message);
-        }
-
         /**
          *    Uses a stack trace to find the line of an assertion.
-         *    @param string $format    String formatting.
-         *    @param array $stack      Stack frames top most first. Only
-         *                             needed if not using the PHP
-         *                             backtrace function.
          *    @return string           Line number of first assert*
          *                             method embedded in format string.
          *    @access public
          */
-        function getAssertionLine($stack = false) {
-            if ($stack === false) {
-                $stack = SimpleTestCompatibility::getStackTrace();
-            }
-            return SimpleDumper::getFormattedAssertionLine($stack);
+        function getAssertionLine() {
+            $trace = new SimpleStackTrace(array('assert', 'expect', 'pass', 'fail', 'skip'));
+            return $trace->traceMethod();
         }
 
         /**
          *    @access public
          */
         function dump($variable, $message = false) {
-            $formatted = SimpleDumper::dump($variable);
+            $dumper = $this->_reporter->getDumper();
+            $formatted = $dumper->dump($variable);
             if ($message) {
                 $formatted = $message . "\n" . $formatted;
             }
         }
 
         /**
-         *    Dispatches a text message straight to the
-         *    test suite. Useful for status bar displays.
-         *    @param string $message        Message to show.
-         *    @access public
+         *    @deprecated
          */
         function sendMessage($message) {
             $this->_reporter->PaintMessage($message);
         *    @package          SimpleTest
         *    @subpackage       UnitTester
      */
-    class GroupTest {
+    class TestSuite {
         var $_label;
         var $_test_cases;
         var $_old_track_errors;
          *                            of the test.
          *    @access public
          */
-        function GroupTest($label = false) {
+        function TestSuite($label = false) {
             $this->_label = $label ? $label : get_class($this);
             $this->_test_cases = array();
             $this->_old_track_errors = ini_get('track_errors');
          *    @access public
          */
         function addTestClass($class) {
-            if ($this->_getBaseTestCase($class) == 'grouptest') {
+            if ($this->_getBaseTestCase($class) == 'testsuite' || $this->_getBaseTestCase($class) == 'grouptest') {
                 $this->_test_cases[] = &new $class();
             } else {
                 $this->_test_cases[] = $class;
         function addTestFile($test_file) {
             $existing_classes = get_declared_classes();
             if ($error = $this->_requireWithError($test_file)) {
-                $this->addTestCase(new BadGroupTest($test_file, $error));
+                $this->addTestCase(new BadTestSuite($test_file, $error));
                 return;
             }
             $classes = $this->_selectRunnableTests($existing_classes, get_declared_classes());
             if (count($classes) == 0) {
-                $this->addTestCase(new BadGroupTest($test_file, "No runnable test cases in [$test_file]"));
+                $this->addTestCase(new BadTestSuite($test_file, "No runnable test cases in [$test_file]"));
                 return;
             }
             $group = &$this->_createGroupFromClasses($test_file, $classes);
             $error = isset($php_errormsg) ? $php_errormsg : false;
             $this->_disableErrorReporting();
             $self_inflicted_errors = array(
-                    'Assigning the return value of new by reference is deprecated',
-                    'var: Deprecated. Please use the public/private/protected modifiers');
-            if (in_array($error, $self_inflicted_errors)) {
-                return false;
-            }
+                    '/Assigning the return value of new by reference/i',
+                    '/var: Deprecated/i',
+                                       '/Non-static method/i');
+            foreach ($self_inflicted_errors as $pattern) {
+                               if (preg_match($pattern, $error)) {
+                                       return false;
+                               }
+                       }
             return $error;
         }
 
          *    Builds a group test from a class list.
          *    @param string $title       Title of new group.
          *    @param array $classes      Test classes.
-         *    @return GroupTest          Group loaded with the new
+         *    @return TestSuite          Group loaded with the new
          *                               test cases.
          *    @access private
          */
         function &_createGroupFromClasses($title, $classes) {
             SimpleTest::ignoreParentsIfIgnored($classes);
-            $group = &new GroupTest($title);
+            $group = &new TestSuite($title);
             foreach ($classes as $class) {
                 if (! SimpleTest::isIgnored($class)) {
                     $group->addTestClass($class);
         function _getBaseTestCase($class) {
             while ($class = get_parent_class($class)) {
                 $class = strtolower($class);
-                if ($class == "simpletestcase" || $class == "grouptest") {
+                if ($class == 'simpletestcase' || $class == 'testsuite' || $class == 'grouptest') {
                     return $class;
                 }
             }
                     $class = $this->_test_cases[$i];
                     $test = &new $class();
                     $test->run($reporter);
+                    unset($test);
                 } else {
                     $this->_test_cases[$i]->run($reporter);
                 }
             return $count;
         }
     }
+    
+    /**
+     *    @deprecated
+     */
+    class GroupTest extends TestSuite { }
 
     /**
      *    This is a failing group test for when a test suite hasn't
         *    @package          SimpleTest
         *    @subpackage       UnitTester
      */
-    class BadGroupTest {
+    class BadTestSuite {
         var $_label;
         var $_error;
 
          *                            of the test.
          *    @access public
          */
-        function BadGroupTest($label, $error) {
+        function BadTestSuite($label, $error) {
             $this->_label = $label;
             $this->_error = $error;
         }
          */
         function run(&$reporter) {
             $reporter->paintGroupStart($this->getLabel(), $this->getSize());
-            $reporter->paintFail('Bad GroupTest [' . $this->getLabel() .
+            $reporter->paintFail('Bad TestSuite [' . $this->getLabel() .
                     '] with error [' . $this->_error . ']');
             $reporter->paintGroupEnd($this->getLabel());
             return $reporter->getStatus();
             return 0;
         }
     }
-?>
+    
+    /**
+     *    @deprecated
+     */
+    class BadGroupTest extends BadTestSuite { }
+?>
\ No newline at end of file
index bbfe02bb18d7571bdbe6b3729bc4dbe69ea5cbdb..6efaf60d55fe19a2b5e0d91c0127a1e401a9a7c0 100644 (file)
             $this->SimpleTestCase($label);
         }
 
+        /**
+         *    Called from within the test methods to register
+         *    passes and failures.
+         *    @param boolean $result    Pass on true.
+         *    @param string $message    Message to display describing
+         *                              the test state.
+         *    @return boolean           True on pass
+         *    @access public
+         */
+        function assertTrue($result, $message = false) {
+            return $this->assert(new TrueExpectation(), $result, $message);
+        }
+
+        /**
+         *    Will be true on false and vice versa. False
+         *    is the PHP definition of false, so that null,
+         *    empty strings, zero and an empty array all count
+         *    as false.
+         *    @param boolean $result    Pass on false.
+         *    @param string $message    Message to display.
+         *    @return boolean           True on pass
+         *    @access public
+         */
+        function assertFalse($result, $message = '%s') {
+            return $this->assert(new FalseExpectation(), $result, $message);
+        }
+
         /**
          *    Will be true if the value is null.
          *    @param null $value       Supposedly null value.
          *    @return boolean                        True on pass
          *    @access public
          */
-        function assertNull($value, $message = "%s") {
+        function assertNull($value, $message = '%s') {
             $dumper = &new SimpleDumper();
             $message = sprintf(
                     $message,
-                    "[" . $dumper->describeValue($value) . "] should be null");
+                    '[' . $dumper->describeValue($value) . '] should be null');
             return $this->assertTrue(! isset($value), $message);
         }
 
          *    @return boolean               True on pass.
          *    @access public
          */
-        function assertNotNull($value, $message = "%s") {
+        function assertNotNull($value, $message = '%s') {
             $dumper = &new SimpleDumper();
             $message = sprintf(
                     $message,
-                    "[" . $dumper->describeValue($value) . "] should not be null");
+                    '[' . $dumper->describeValue($value) . '] should not be null');
             return $this->assertTrue(isset($value), $message);
         }
 
          *    @return boolean              True on pass.
          *    @access public
          */
-        function assertIsA($object, $type, $message = "%s") {
+        function assertIsA($object, $type, $message = '%s') {
             return $this->assert(
                     new IsAExpectation($type),
                     $object,
          *    @return boolean              True on pass.
          *    @access public
          */
-        function assertNotA($object, $type, $message = "%s") {
+        function assertNotA($object, $type, $message = '%s') {
             return $this->assert(
                     new NotAExpectation($type),
                     $object,
          *    @return boolean              True on pass
          *    @access public
          */
-        function assertEqual($first, $second, $message = "%s") {
+        function assertEqual($first, $second, $message = '%s') {
             return $this->assert(
                     new EqualExpectation($first),
                     $second,
          *    @return boolean               True on pass
          *    @access public
          */
-        function assertNotEqual($first, $second, $message = "%s") {
+        function assertNotEqual($first, $second, $message = '%s') {
             return $this->assert(
                     new NotEqualExpectation($first),
                     $second,
          *    @return boolean              True on pass
          *    @access public
          */
-        function assertWithinMargin($first, $second, $margin, $message = "%s") {
+        function assertWithinMargin($first, $second, $margin, $message = '%s') {
             return $this->assert(
                     new WithinMarginExpectation($first, $margin),
                     $second,
          *    @return boolean              True on pass
          *    @access public
          */
-        function assertOutsideMargin($first, $second, $margin, $message = "%s") {
+        function assertOutsideMargin($first, $second, $margin, $message = '%s') {
             return $this->assert(
                     new OutsideMarginExpectation($first, $margin),
                     $second,
          *    @return boolean               True on pass
          *    @access public
          */
-        function assertIdentical($first, $second, $message = "%s") {
+        function assertIdentical($first, $second, $message = '%s') {
             return $this->assert(
                     new IdenticalExpectation($first),
                     $second,
          *    @return boolean               True on pass
          *    @access public
          */
-        function assertNotIdentical($first, $second, $message = "%s") {
+        function assertNotIdentical($first, $second, $message = '%s') {
             return $this->assert(
                     new NotIdenticalExpectation($first),
                     $second,
          *    @return boolean               True on pass
          *    @access public
          */
-        function assertReference(&$first, &$second, $message = "%s") {
+        function assertReference(&$first, &$second, $message = '%s') {
             $dumper = &new SimpleDumper();
             $message = sprintf(
                     $message,
-                    "[" . $dumper->describeValue($first) .
-                            "] and [" . $dumper->describeValue($second) .
-                            "] should reference the same object");
+                    '[' . $dumper->describeValue($first) .
+                            '] and [' . $dumper->describeValue($second) .
+                            '] should reference the same object');
             return $this->assertTrue(
                     SimpleTestCompatibility::isReference($first, $second),
                     $message);
          *    @return boolean               True on pass
          *    @access public
          */
-        function assertClone(&$first, &$second, $message = "%s") {
+        function assertClone(&$first, &$second, $message = '%s') {
             $dumper = &new SimpleDumper();
             $message = sprintf(
                     $message,
-                    "[" . $dumper->describeValue($first) .
-                            "] and [" . $dumper->describeValue($second) .
-                            "] should not be the same object");
+                    '[' . $dumper->describeValue($first) .
+                            '] and [' . $dumper->describeValue($second) .
+                            '] should not be the same object');
             $identical = &new IdenticalExpectation($first);
             return $this->assertTrue(
                     $identical->test($second) &&
          *    @return boolean           True on pass
          *    @access public
          */
-        function assertPattern($pattern, $subject, $message = "%s") {
+        function assertPattern($pattern, $subject, $message = '%s') {
             return $this->assert(
                     new PatternExpectation($pattern),
                     $subject,
         /**
          *       @deprecated
          */
-        function assertWantedPattern($pattern, $subject, $message = "%s") {
+        function assertWantedPattern($pattern, $subject, $message = '%s') {
                return $this->assertPattern($pattern, $subject, $message);
         }
 
          *    @return boolean           True on pass
          *    @access public
          */
-        function assertNoPattern($pattern, $subject, $message = "%s") {
+        function assertNoPattern($pattern, $subject, $message = '%s') {
             return $this->assert(
                     new NoPatternExpectation($pattern),
                     $subject,
         /**
          *       @deprecated
          */
-        function assertNoUnwantedPattern($pattern, $subject, $message = "%s") {
+        function assertNoUnwantedPattern($pattern, $subject, $message = '%s') {
                return $this->assertNoPattern($pattern, $subject, $message);
         }
 
         /**
-         *    Confirms that no errors have occoured so
-         *    far in the test method.
-         *    @param string $message    Message to display.
-         *    @return boolean           True on pass
+         *    @deprecated
+         */
+        function swallowErrors() {
+            $context = &SimpleTest::getContext();
+            $queue = &$context->get('SimpleErrorQueue');
+            $queue->clear();
+        }
+
+        /**
+         *    @deprecated
+         */
+        function assertNoErrors($message = '%s') {
+            $context = &SimpleTest::getContext();
+            $queue = &$context->get('SimpleErrorQueue');
+            return $queue->assertNoErrors($message);
+        }
+
+        /**
+         *    @deprecated
+         */
+        function assertError($expected = false, $message = '%s') {
+            $context = &SimpleTest::getContext();
+            $queue = &$context->get('SimpleErrorQueue');
+            return $queue->assertError($this->_coerceExpectation($expected), $message);
+        }
+
+        /**
+         *    Prepares for an error. If the error mismatches it
+         *    passes through, otherwise it is swallowed. Any
+         *    left over errors trigger failures.
+         *    @param SimpleExpectation/string $expected   The error to match.
+         *    @param string $message                      Message on failure.
          *    @access public
          */
-        function assertNoErrors($message = "%s") {
-            $queue = &SimpleErrorQueue::instance();
-            return $this->assertTrue(
-                    $queue->isEmpty(),
-                    sprintf($message, "Should be no errors"));
+        function expectError($expected = false, $message = '%s') {
+            $context = &SimpleTest::getContext();
+            $queue = &$context->get('SimpleErrorQueue');
+            $queue->expectError($this->_coerceExpectation($expected), $message);
         }
 
         /**
-         *    Confirms that an error has occoured and
-         *    optionally that the error text matches exactly.
-         *    @param string $expected   Expected error text or
-         *                              false for no check.
-         *    @param string $message    Message to display.
-         *    @return boolean           True on pass
+         *    Prepares for an exception. If the error mismatches it
+         *    passes through, otherwise it is swallowed. Any
+         *    left over errors trigger failures.
+         *    @param SimpleExpectation/Exception $expected  The error to match.
+         *    @param string $message                        Message on failure.
          *    @access public
          */
-        function assertError($expected = false, $message = "%s") {
-            $queue = &SimpleErrorQueue::instance();
-            if ($queue->isEmpty()) {
-                $this->fail(sprintf($message, "Expected error not found"));
-                return;
-            }
-            list($severity, $content, $file, $line, $globals) = $queue->extract();
-            $severity = SimpleErrorQueue::getSeverityAsString($severity);
-            if (! $expected) {
-                return $this->pass(
-                        "Captured a PHP error of [$content] severity [$severity] in [$file] line [$line] -> %s");
-            }
-            $expected = $this->_coerceToExpectation($expected);
-            return $this->assert(
-                    $expected,
-                    $content,
-                    "Expected PHP error [$content] severity [$severity] in [$file] line [$line] -> %s");
+        function expectException($expected = false, $message = '%s') {
+            $context = &SimpleTest::getContext();
+            $queue = &$context->get('SimpleExceptionTrap');
+            $queue->expectException($expected, $message . $this->getAssertionLine());
         }
 
         /**
          *    @return SimpleExpectation   Expectation object.
          *    @access private
          */
-        function _coerceToExpectation($expected) {
+        function _coerceExpectation($expected) {
+            if ($expected == false) {
+                return new AnythingExpectation();
+            }
             if (SimpleTestCompatibility::isA($expected, 'SimpleExpectation')) {
                 return $expected;
             }
+                       if(is_string($expected)) {
+                               $expected = str_replace('%', '%%', $expected);
+                       }
             return new EqualExpectation($expected);
         }
 
         /**
          *    @deprecated
          */
-        function assertErrorPattern($pattern, $message = "%s") {
+        function assertErrorPattern($pattern, $message = '%s') {
             return $this->assertError(new PatternExpectation($pattern), $message);
         }
     }
-?>
+?>
\ No newline at end of file
index 9a68156eeaa73b995976f09376f46e312faece67..b66b31f43c0ca137384f16c3ba5d815b162dcd94 100644 (file)
          *    @access public
          */
         function normalisePath($path) {
-            $path = preg_replace('|/[^/]+/\.\./|', '/', $path);
-            return preg_replace('|/\./|', '/', $path);
+            $path = preg_replace('|/\./|', '/', $path);
+            return preg_replace('|/[^/]+/\.\./|', '/', $path);
         }
         
         /**
index c98507deef735dde72d96c2c125d4ef8bebfe165..be424bb175c4f531b5f1a5e6338d123e901469b3 100644 (file)
             } else {
                 return "Field expectation [" . $dumper->describeValue($this->_value) .
                         "] fails with [" .
-                        $this->_dumper->describeValue($compare) . "] " .
-                        $this->_dumper->describeDifference($this->_value, $compare);
+                        $dumper->describeValue($compare) . "] " .
+                        $dumper->describeDifference($this->_value, $compare);
             }
         }
     }
          */
         function testMessage($compare) {
             if (SimpleExpectation::isExpectation($this->_expected_value)) {
-                $message = $this->_expected_value->testMessage($compare);
+                $message = $this->_expected_value->overlayMessage($compare, $this->_getDumper());
             } else {
                 $message = $this->_expected_header .
                         ($this->_expected_value ? ': ' . $this->_expected_value : '');
          *    @param string $expiry        Expiry date.
          *    @access public
          */
-        function setCookie($name, $value, $host = false, $path = "/", $expiry = false) {
+        function setCookie($name, $value, $host = false, $path = '/', $expiry = false) {
             $this->_browser->setCookie($name, $value, $host, $path, $expiry);
         }
         
             return $this->_failOnError($this->_browser->click($label));
         }
         
+        /**
+         *    Checks for a click target.
+         *    @param string $label        Visible text or alt text.
+         *    @return boolean             True if click target.
+         *    @access public
+         */    
+        function assertClickable($label, $message = '%s') {
+            return $this->assertTrue(
+                    $this->_browser->isClickable($label),
+                    sprintf($message, "Click target [$label] should exist"));
+        }
+        
         /**
          *    Clicks the submit button by label. The owning
          *    form will be submitted by this.
                     $this->_browser->clickSubmitById($id, $additional));
         }
         
+        /**
+         *    Checks for a valid button label.
+         *    @param string $label        Visible text.
+         *    @return boolean             True if click target.
+         *    @access public
+         */    
+        function assertSubmit($label, $message = '%s') {
+            return $this->assertTrue(
+                    $this->_browser->isSubmit($label),
+                    sprintf($message, "Submit button [$label] should exist"));
+        }
+        
         /**
          *    Clicks the submit image by some kind of label. Usually
          *    the alt tag or the nearest equivalent. The owning
                     $this->_browser->clickImageById($id, $x, $y, $additional));
         }
         
+        /**
+         *    Checks for a valid image with atht alt text or title.
+         *    @param string $label        Visible text.
+         *    @return boolean             True if click target.
+         *    @access public
+         */    
+        function assertImage($label, $message = '%s') {
+            return $this->assertTrue(
+                    $this->_browser->isImage($label),
+                    sprintf($message, "Image with text [$label] should exist"));
+        }
+        
         /**
          *    Submits a form by the ID.
          *    @param string $id       Form ID. No button information
             return $this->_failOnError($this->_browser->clickLinkById($id));
         }
         
-        /**
-         *    Will trigger a pass if the two parameters have
-         *    the same value only. Otherwise a fail. This
-         *    is for testing hand extracted text, etc.
-         *    @param mixed $first          Value to compare.
-         *    @param mixed $second         Value to compare.
-         *    @param string $message       Message to display.
-         *    @return boolean              True on pass
-         *    @access public
-         */
-        function assertEqual($first, $second, $message = "%s") {
-            return $this->assert(
-                    new EqualExpectation($first),
-                    $second,
-                    $message);
-        }
-        
-        /**
-         *    Will trigger a pass if the two parameters have
-         *    a different value. Otherwise a fail. This
-         *    is for testing hand extracted text, etc.
-         *    @param mixed $first           Value to compare.
-         *    @param mixed $second          Value to compare.
-         *    @param string $message        Message to display.
-         *    @return boolean               True on pass
-         *    @access public
-         */
-        function assertNotEqual($first, $second, $message = "%s") {
-            return $this->assert(
-                    new NotEqualExpectation($first),
-                    $second,
-                    $message);
-        }
-        
         /**
          *    Tests for the presence of a link label. Match is
          *    case insensitive with normalised space.
          *    @param string $label     Text between the anchor tags.
+         *    @param mixed $expected   Expected URL or expectation object.
          *    @param string $message   Message to display. Default
          *                             can be embedded with %s.
          *    @return boolean          True if link present.
          *    @access public
          */
-        function assertLink($label, $message = "%s") {
-            return $this->assertTrue(
-                    $this->_browser->isLink($label),
-                    sprintf($message, "Link [$label] should exist"));
+        function assertLink($label, $expected = true, $message = '%s') {
+            $url = $this->_browser->getLink($label);
+            if ($expected === true) {
+                return $this->assertTrue($url !== false, sprintf($message, "Link [$label] should exist"));
+            }
+            if (! SimpleExpectation::isExpectation($expected)) {
+                $expected = new IdenticalExpectation($expected);
+            }
+            return $this->assert($expected, $url->asString(), sprintf($message, "Link [$label] should match"));
         }
 
         /**
          *    @return boolean                 True if link missing.
          *    @access public
          */
-        function assertNoLink($label, $message = "%s") {
-            return $this->assertFalse(
-                    $this->_browser->isLink($label),
+        function assertNoLink($label, $message = '%s') {
+            return $this->assertTrue(
+                    $this->_browser->getLink($label) === false,
                     sprintf($message, "Link [$label] should not exist"));
         }
         
         /**
          *    Tests for the presence of a link id attribute.
          *    @param string $id        Id attribute value.
+         *    @param mixed $expected   Expected URL or expectation object.
          *    @param string $message   Message to display. Default
          *                             can be embedded with %s.
          *    @return boolean          True if link present.
          *    @access public
          */
-        function assertLinkById($id, $message = "%s") {
-            return $this->assertTrue(
-                    $this->_browser->isLinkById($id),
-                    sprintf($message, "Link ID [$id] should exist"));
+        function assertLinkById($id, $expected = true, $message = '%s') {
+            $url = $this->_browser->getLinkById($id);
+            if ($expected === true) {
+                return $this->assertTrue($url !== false, sprintf($message, "Link ID [$id] should exist"));
+            }
+            if (! SimpleExpectation::isExpectation($expected)) {
+                $expected = new IdenticalExpectation($expected);
+            }
+            return $this->assert($expected, $url->asString(), sprintf($message, "Link ID [$id] should match"));
         }
 
         /**
          *    @return boolean          True if link missing.
          *    @access public
          */
-        function assertNoLinkById($id, $message = "%s") {
-            return $this->assertFalse(
-                    $this->_browser->isLinkById($id),
+        function assertNoLinkById($id, $message = '%s') {
+            return $this->assertTrue(
+                    $this->_browser->getLinkById($id) === false,
                     sprintf($message, "Link ID [$id] should not exist"));
         }
         
         
         /**
          *    Tests the text between the title tags.
-         *    @param string $title      Expected title.
-         *    @param string $message    Message to display.
-         *    @return boolean           True if pass.
+         *    @param string/SimpleExpectation $title    Expected title.
+         *    @param string $message                    Message to display.
+         *    @return boolean                           True if pass.
          *    @access public
          */
         function assertTitle($title = false, $message = '%s') {
                     $this->getCookie($name) === false,
                     sprintf($message, "Not expecting cookie [$name]"));
         }
+
+        /**
+         *    Called from within the test methods to register
+         *    passes and failures.
+         *    @param boolean $result    Pass on true.
+         *    @param string $message    Message to display describing
+         *                              the test state.
+         *    @return boolean           True on pass
+         *    @access public
+         */
+        function assertTrue($result, $message = false) {
+            return $this->assert(new TrueExpectation(), $result, $message);
+        }
+
+        /**
+         *    Will be true on false and vice versa. False
+         *    is the PHP definition of false, so that null,
+         *    empty strings, zero and an empty array all count
+         *    as false.
+         *    @param boolean $result    Pass on false.
+         *    @param string $message    Message to display.
+         *    @return boolean           True on pass
+         *    @access public
+         */
+        function assertFalse($result, $message = '%s') {
+            return $this->assert(new FalseExpectation(), $result, $message);
+        }
+        
+        /**
+         *    Will trigger a pass if the two parameters have
+         *    the same value only. Otherwise a fail. This
+         *    is for testing hand extracted text, etc.
+         *    @param mixed $first          Value to compare.
+         *    @param mixed $second         Value to compare.
+         *    @param string $message       Message to display.
+         *    @return boolean              True on pass
+         *    @access public
+         */
+        function assertEqual($first, $second, $message = '%s') {
+            return $this->assert(
+                    new EqualExpectation($first),
+                    $second,
+                    $message);
+        }
+        
+        /**
+         *    Will trigger a pass if the two parameters have
+         *    a different value. Otherwise a fail. This
+         *    is for testing hand extracted text, etc.
+         *    @param mixed $first           Value to compare.
+         *    @param mixed $second          Value to compare.
+         *    @param string $message        Message to display.
+         *    @return boolean               True on pass
+         *    @access public
+         */
+        function assertNotEqual($first, $second, $message = '%s') {
+            return $this->assert(
+                    new NotEqualExpectation($first),
+                    $second,
+                    $message);
+        }
+
+        /**
+         *    Uses a stack trace to find the line of an assertion.
+         *    @return string           Line number of first assert*
+         *                             method embedded in format string.
+         *    @access public
+         */
+        function getAssertionLine() {
+            $trace = new SimpleStackTrace(array('assert', 'click', 'pass', 'fail'));
+            return $trace->traceMethod();
+        }
     }
 ?>
\ No newline at end of file
index c6158cf5ef37876808199df6b70cea3791a1bf4f..0de9a5ebe82984f6b94eb3d399a730a7c5608f3d 100644 (file)
@@ -23,7 +23,9 @@
         var $_namespace;
 
         /**
-         *    Does nothing yet.
+         *    Sets up indentation and namespace.
+         *    @param string $namespace        Namespace to add to each tag.
+         *    @param string $indent           Indenting to add on each nesting.
          *    @access public
          */
         function XmlReporter($namespace = false, $indent = '  ') {
         }
 
         /**
-         *    Increments the pass count.
-         *    @param string $message        Message is ignored.
+                *    Paints pass as XML.
+         *    @param string $message        Message to encode.
          *    @access public
          */
         function paintPass($message) {
         }
 
         /**
-         *    Increments the fail count.
-         *    @param string $message        Message is ignored.
+                *    Paints failure as XML.
+         *    @param string $message        Message to encode.
          *    @access public
          */
         function paintFail($message) {
         }
 
         /**
-         *    Paints a PHP error or exception.
-         *    @param string $message        Message is ignored.
+                *    Paints error as XML.
+         *    @param string $message        Message to encode.
          *    @access public
-         *    @abstract
          */
         function paintError($message) {
             parent::paintError($message);
             print "</" . $this->_namespace . "exception>\n";
         }
 
+        /**
+                *    Paints exception as XML.
+         *    @param Exception $exception    Exception to encode.
+         *    @access public
+         */
+        function paintException($exception) {
+            parent::paintException($exception);
+            print $this->_getIndent(1);
+            print "<" . $this->_namespace . "exception>";
+            $message = 'Unexpected exception of type [' . get_class($exception) .
+                    '] with message ['. $exception->getMessage() .
+                    '] in ['. $exception->getFile() .
+                    ' line ' . $exception->getLine() . ']';
+            print $this->toParsedXml($message);
+            print "</" . $this->_namespace . "exception>\n";
+        }
+
+        /**
+         *    Paints the skipping message and tag.
+         *    @param string $message        Text to display in skip tag.
+         *    @access public
+         */
+        function paintSkip($message) {
+            parent::paintSkip($message);
+            print $this->_getIndent(1);
+            print "<" . $this->_namespace . "skip>";
+            print $this->toParsedXml($message);
+            print "</" . $this->_namespace . "skip>\n";
+        }
+
         /**
          *    Paints a simple supplementary message.
          *    @param string $message        Text to display.
          */
         function _isLeaf($tag) {
             return in_array($tag, array(
-                    'NAME', 'PASS', 'FAIL', 'EXCEPTION', 'MESSAGE', 'FORMATTED', 'SIGNAL'));
+                    'NAME', 'PASS', 'FAIL', 'EXCEPTION', 'SKIP', 'MESSAGE', 'FORMATTED', 'SIGNAL'));
         }
 
         /**
                 $this->_listener->paintFail($this->_content);
             } elseif ($tag == 'EXCEPTION') {
                 $this->_listener->paintError($this->_content);
+            } elseif ($tag == 'SKIP') {
+                $this->_listener->paintSkip($this->_content);
             } elseif ($tag == 'SIGNAL') {
                 $this->_listener->paintSignal(
                         $this->_attributes['TYPE'],