diff options
Diffstat (limited to 'vendors/simpletest/test_case.php')
-rw-r--r-- | vendors/simpletest/test_case.php | 708 |
1 files changed, 708 insertions, 0 deletions
diff --git a/vendors/simpletest/test_case.php b/vendors/simpletest/test_case.php new file mode 100644 index 000000000..e5b229833 --- /dev/null +++ b/vendors/simpletest/test_case.php @@ -0,0 +1,708 @@ +<?php +/** + * Base include file for SimpleTest + * @package SimpleTest + * @subpackage UnitTester + * @version $Id: test_case.php 1726 2008-04-08 01:20:10Z lastcraft $ + */ + +/**#@+ + * Includes SimpleTest files and defined the root constant + * for dependent libraries. + */ +require_once(dirname(__FILE__) . '/invoker.php'); +require_once(dirname(__FILE__) . '/errors.php'); +require_once(dirname(__FILE__) . '/compatibility.php'); +require_once(dirname(__FILE__) . '/scorer.php'); +require_once(dirname(__FILE__) . '/expectation.php'); +require_once(dirname(__FILE__) . '/dumper.php'); +require_once(dirname(__FILE__) . '/simpletest.php'); +if (version_compare(phpversion(), '5') >= 0) { + require_once(dirname(__FILE__) . '/exceptions.php'); + require_once(dirname(__FILE__) . '/reflection_php5.php'); +} else { + require_once(dirname(__FILE__) . '/reflection_php4.php'); +} +if (! defined('SIMPLE_TEST')) { + /** + * @ignore + */ + define('SIMPLE_TEST', dirname(__FILE__) . DIRECTORY_SEPARATOR); +} +/**#@-*/ + +/** + * Basic test case. This is the smallest unit of a test + * suite. It searches for + * all methods that start with the the string "test" and + * runs them. Working test cases extend this class. + * @package SimpleTest + * @subpackage UnitTester + */ +class SimpleTestCase { + var $_label = false; + var $_reporter; + var $_observers; + var $_should_skip = false; + + /** + * Sets up the test with no display. + * @param string $label If no test name is given then + * the class name is used. + * @access public + */ + function SimpleTestCase($label = false) { + if ($label) { + $this->_label = $label; + } + } + + /** + * Accessor for the test name for subclasses. + * @return string Name of the test. + * @access public + */ + function getLabel() { + 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. + * @access public + */ + function &createInvoker() { + $invoker = &new SimpleErrorTrappingInvoker(new SimpleInvoker($this)); + if (version_compare(phpversion(), '5') >= 0) { + $invoker = &new SimpleExceptionTrappingInvoker($invoker); + } + return $invoker; + } + + /** + * Uses reflection to run every method within itself + * 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) { + $context = &SimpleTest::getContext(); + $context->setTest($this); + $context->setReporter($reporter); + $this->_reporter = &$reporter; + $started = false; + foreach ($this->getTests() as $method) { + if ($reporter->shouldInvoke($this->getLabel(), $method)) { + $this->skip(); + if ($this->_should_skip) { + break; + } + if (! $started) { + $reporter->paintCaseStart($this->getLabel()); + $started = true; + } + $invoker = &$this->_reporter->createInvoker($this->createInvoker()); + $invoker->before($method); + $invoker->invoke($method); + $invoker->after($method); + } + } + if ($started) { + $reporter->paintCaseEnd($this->getLabel()); + } + unset($this->_reporter); + return $reporter->getStatus(); + } + + /** + * Gets a list of test names. Normally that will + * be all internal methods that start with the + * name "test". This method should be overridden + * if you want a different rule. + * @return array List of test names. + * @access public + */ + function getTests() { + $methods = array(); + foreach (get_class_methods(get_class($this)) as $method) { + if ($this->_isTest($method)) { + $methods[] = $method; + } + } + return $methods; + } + + /** + * Tests to see if the method is a test that should + * be run. Currently any method that starts with 'test' + * is a candidate unless it is the constructor. + * @param string $method Method name to try. + * @return boolean True if test method. + * @access protected + */ + function _isTest($method) { + if (strtolower(substr($method, 0, 4)) == 'test') { + return ! SimpleTestCompatibility::isA($this, strtolower($method)); + } + return false; + } + + /** + * Announces the start of the test. + * @param string $method Test method just started. + * @access public + */ + function before($method) { + $this->_reporter->paintMethodStart($method); + $this->_observers = array(); + } + + /** + * Sets up unit test wide variables at the start + * of each test method. To be overridden in + * actual user test cases. + * @access public + */ + function setUp() { + } + + /** + * Clears the data set in the setUp() method call. + * To be overridden by the user in actual user test cases. + * @access public + */ + function tearDown() { + } + + /** + * Announces the end of the test. Includes private clean up. + * @param string $method Test method just finished. + * @access public + */ + function after($method) { + for ($i = 0; $i < count($this->_observers); $i++) { + $this->_observers[$i]->atTestEnd($method, $this); + } + $this->_reporter->paintMethodEnd($method); + } + + /** + * Sets up an observer for the test end. + * @param object $observer Must have atTestEnd() + * method. + * @access public + */ + function tell(&$observer) { + $this->_observers[] = &$observer; + } + + /** + * @deprecated + */ + function pass($message = "Pass") { + if (! isset($this->_reporter)) { + trigger_error('Can only make assertions within test methods'); + } + $this->_reporter->paintPass( + $message . $this->getAssertionLine()); + return true; + } + + /** + * Sends a fail event with a message. + * @param string $message Message to send. + * @access public + */ + function fail($message = "Fail") { + if (! isset($this->_reporter)) { + trigger_error('Can only make assertions within test methods'); + } + $this->_reporter->paintFail( + $message . $this->getAssertionLine()); + return false; + } + + /** + * Formats a PHP error and dispatches it to the + * reporter. + * @param integer $severity PHP error code. + * @param string $message Text of error. + * @param string $file File error occoured in. + * @param integer $line Line number of error. + * @access public + */ + function error($severity, $message, $file, $line) { + 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]"); + } + + /** + * Formats an exception and dispatches it to the + * reporter. + * @param Exception $exception Object thrown. + * @access public + */ + function exception($exception) { + $this->_reporter->paintException($exception); + } + + /** + * @deprecated + */ + function signal($type, &$payload) { + if (! isset($this->_reporter)) { + trigger_error('Can only make assertions within test methods'); + } + $this->_reporter->paintSignal($type, $payload); + } + + /** + * Runs an expectation directly, for extending the + * tests with new expectation classes. + * @param SimpleExpectation $expectation Expectation subclass. + * @param mixed $compare Value to compare. + * @param string $message Message to display. + * @return boolean True on pass + * @access public + */ + function assert(&$expectation, $compare, $message = '%s') { + 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()))); + } + } + + /** + * @deprecated + */ + function assertExpectation(&$expectation, $compare, $message = '%s') { + return $this->assert($expectation, $compare, $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', 'expect', 'pass', 'fail', 'skip')); + return $trace->traceMethod(); + } + + /** + * Sends a formatted dump of a variable to the + * test suite for those emergency debugging + * situations. + * @param mixed $variable Variable to display. + * @param string $message Message to display. + * @return mixed The original variable. + * @access public + */ + function dump($variable, $message = false) { + $dumper = $this->_reporter->getDumper(); + $formatted = $dumper->dump($variable); + if ($message) { + $formatted = $message . "\n" . $formatted; + } + $this->_reporter->paintFormattedMessage($formatted); + return $variable; + } + + /** + * @deprecated + */ + function sendMessage($message) { + $this->_reporter->PaintMessage($message); + } + + /** + * Accessor for the number of subtests including myelf. + * @return integer Number of test cases. + * @access public + * @static + */ + function getSize() { + return 1; + } +} + +/** + * Helps to extract test cases automatically from a file. + */ +class SimpleFileLoader { + + /** + * Builds a test suite from a library of test cases. + * The new suite is composed into this one. + * @param string $test_file File name of library with + * test case classes. + * @return TestSuite The new test suite. + * @access public + */ + function &load($test_file) { + $existing_classes = get_declared_classes(); + $existing_globals = get_defined_vars(); + include_once($test_file); + $new_globals = get_defined_vars(); + $this->_makeFileVariablesGlobal($existing_globals, $new_globals); + $new_classes = array_diff(get_declared_classes(), $existing_classes); + if (empty($new_classes)) { + $new_classes = $this->_scrapeClassesFromFile($test_file); + } + $classes = $this->selectRunnableTests($new_classes); + $suite = &$this->createSuiteFromClasses($test_file, $classes); + return $suite; + } + + /** + * Imports new variables into the global namespace. + * @param hash $existing Variables before the file was loaded. + * @param hash $new Variables after the file was loaded. + * @access private + */ + function _makeFileVariablesGlobal($existing, $new) { + $globals = array_diff(array_keys($new), array_keys($existing)); + foreach ($globals as $global) { + $_GLOBALS[$global] = $new[$global]; + } + } + + /** + * Lookup classnames from file contents, in case the + * file may have been included before. + * Note: This is probably too clever by half. Figuring this + * out after a failed test case is going to be tricky for us, + * never mind the user. A test case should not be included + * twice anyway. + * @param string $test_file File name with classes. + * @access private + */ + function _scrapeClassesFromFile($test_file) { + preg_match_all('~^\s*class\s+(\w+)(\s+(extends|implements)\s+\w+)*\s*\{~mi', + file_get_contents($test_file), + $matches ); + return $matches[1]; + } + + /** + * Calculates the incoming test cases. Skips abstract + * and ignored classes. + * @param array $candidates Candidate classes. + * @return array New classes which are test + * cases that shouldn't be ignored. + * @access public + */ + function selectRunnableTests($candidates) { + $classes = array(); + foreach ($candidates as $class) { + if (TestSuite::getBaseTestCase($class)) { + $reflection = new SimpleReflection($class); + if ($reflection->isAbstract()) { + SimpleTest::ignore($class); + } else { + $classes[] = $class; + } + } + } + return $classes; + } + + /** + * Builds a test suite from a class list. + * @param string $title Title of new group. + * @param array $classes Test classes. + * @return TestSuite Group loaded with the new + * test cases. + * @access public + */ + function &createSuiteFromClasses($title, $classes) { + if (count($classes) == 0) { + $suite = &new BadTestSuite($title, "No runnable test cases in [$title]"); + return $suite; + } + SimpleTest::ignoreParentsIfIgnored($classes); + $suite = &new TestSuite($title); + foreach ($classes as $class) { + if (! SimpleTest::isIgnored($class)) { + $suite->addTestClass($class); + } + } + return $suite; + } +} + +/** + * This is a composite test class for combining + * test cases and other RunnableTest classes into + * a group test. + * @package SimpleTest + * @subpackage UnitTester + */ +class TestSuite { + var $_label; + var $_test_cases; + + /** + * Sets the name of the test suite. + * @param string $label Name sent at the start and end + * of the test. + * @access public + */ + function TestSuite($label = false) { + $this->_label = $label; + $this->_test_cases = array(); + } + + /** + * Accessor for the test name for subclasses. If the suite + * wraps a single test case the label defaults to the name of that test. + * @return string Name of the test. + * @access public + */ + function getLabel() { + if (! $this->_label) { + return ($this->getSize() == 1) ? + get_class($this->_test_cases[0]) : get_class($this); + } else { + return $this->_label; + } + } + + /** + * @deprecated + */ + function addTestCase(&$test_case) { + $this->_test_cases[] = &$test_case; + } + + /** + * @deprecated + */ + function addTestClass($class) { + if (TestSuite::getBaseTestCase($class) == 'testsuite') { + $this->_test_cases[] = &new $class(); + } else { + $this->_test_cases[] = $class; + } + } + + /** + * Adds a test into the suite by instance or class. The class will + * be instantiated if it's a test suite. + * @param SimpleTestCase $test_case Suite or individual test + * case implementing the + * runnable test interface. + * @access public + */ + function add(&$test_case) { + if (! is_string($test_case)) { + $this->_test_cases[] = &$test_case; + } elseif (TestSuite::getBaseTestCase($class) == 'testsuite') { + $this->_test_cases[] = &new $class(); + } else { + $this->_test_cases[] = $class; + } + } + + /** + * @deprecated + */ + function addTestFile($test_file) { + $this->addFile($test_file); + } + + /** + * Builds a test suite from a library of test cases. + * The new suite is composed into this one. + * @param string $test_file File name of library with + * test case classes. + * @access public + */ + function addFile($test_file) { + $extractor = new SimpleFileLoader(); + $this->add($extractor->load($test_file)); + } + + /** + * Delegates to a visiting collector to add test + * files. + * @param string $path Path to scan from. + * @param SimpleCollector $collector Directory scanner. + * @access public + */ + function collect($path, &$collector) { + $collector->collect($this, $path); + } + + /** + * Invokes run() on all of the held test cases, instantiating + * them if necessary. + * @param SimpleReporter $reporter Current test reporter. + * @access public + */ + function run(&$reporter) { + $reporter->paintGroupStart($this->getLabel(), $this->getSize()); + for ($i = 0, $count = count($this->_test_cases); $i < $count; $i++) { + if (is_string($this->_test_cases[$i])) { + $class = $this->_test_cases[$i]; + $test = &new $class(); + $test->run($reporter); + unset($test); + } else { + $this->_test_cases[$i]->run($reporter); + } + } + $reporter->paintGroupEnd($this->getLabel()); + return $reporter->getStatus(); + } + + /** + * Number of contained test cases. + * @return integer Total count of cases in the group. + * @access public + */ + function getSize() { + $count = 0; + foreach ($this->_test_cases as $case) { + if (is_string($case)) { + if (! SimpleTest::isIgnored($case)) { + $count++; + } + } else { + $count += $case->getSize(); + } + } + return $count; + } + + /** + * Test to see if a class is derived from the + * SimpleTestCase class. + * @param string $class Class name. + * @access public + * @static + */ + function getBaseTestCase($class) { + while ($class = get_parent_class($class)) { + $class = strtolower($class); + if ($class == 'simpletestcase' || $class == 'testsuite') { + return $class; + } + } + return false; + } +} + +/** + * @package SimpleTest + * @subpackage UnitTester + * @deprecated + */ +class GroupTest extends TestSuite { } + +/** + * This is a failing group test for when a test suite hasn't + * loaded properly. + * @package SimpleTest + * @subpackage UnitTester + */ +class BadTestSuite { + var $_label; + var $_error; + + /** + * Sets the name of the test suite and error message. + * @param string $label Name sent at the start and end + * of the test. + * @access public + */ + function BadTestSuite($label, $error) { + $this->_label = $label; + $this->_error = $error; + } + + /** + * Accessor for the test name for subclasses. + * @return string Name of the test. + * @access public + */ + function getLabel() { + return $this->_label; + } + + /** + * Sends a single error to the reporter. + * @param SimpleReporter $reporter Current test reporter. + * @access public + */ + function run(&$reporter) { + $reporter->paintGroupStart($this->getLabel(), $this->getSize()); + $reporter->paintFail('Bad TestSuite [' . $this->getLabel() . + '] with error [' . $this->_error . ']'); + $reporter->paintGroupEnd($this->getLabel()); + return $reporter->getStatus(); + } + + /** + * Number of contained test cases. Always zero. + * @return integer Total count of cases in the group. + * @access public + */ + function getSize() { + return 0; + } +} + +/** + * @package SimpleTest + * @subpackage UnitTester + * @deprecated + */ +class BadGroupTest extends BadTestSuite { } +?> |