3 * Base include file for SimpleTest
5 * @subpackage UnitTester
6 * @version $Id: test_case.php,v 1.21 2006/02/06 06:13:43 lastcraft Exp $
10 * Includes SimpleTest files and defined the root constant
11 * for dependent libraries.
13 require_once(dirname(__FILE__
) . '/invoker.php');
14 require_once(dirname(__FILE__
) . '/errors.php');
15 require_once(dirname(__FILE__
) . '/compatibility.php');
16 require_once(dirname(__FILE__
) . '/scorer.php');
17 require_once(dirname(__FILE__
) . '/expectation.php');
18 require_once(dirname(__FILE__
) . '/dumper.php');
19 require_once(dirname(__FILE__
) . '/simpletest.php');
20 if (version_compare(phpversion(), '5') >= 0) {
21 require_once(dirname(__FILE__
) . '/exceptions.php');
22 require_once(dirname(__FILE__
) . '/reflection_php5.php');
24 require_once(dirname(__FILE__
) . '/reflection_php4.php');
26 if (! defined('SIMPLE_TEST')) {
30 define('SIMPLE_TEST', dirname(__FILE__
) . '/');
35 * Basic test case. This is the smallest unit of a test
36 * suite. It searches for
37 * all methods that start with the the string "test" and
38 * runs them. Working test cases extend this class.
40 * @subpackage UnitTester
42 class SimpleTestCase
{
48 * Sets up the test with no display.
49 * @param string $label If no test name is given then
50 * the class name is used.
53 function SimpleTestCase($label = false) {
55 $this->_label
= $label;
60 * Accessor for the test name for subclasses.
61 * @return string Name of the test.
65 return $this->_label ?
$this->_label
: get_class($this);
69 * Used to invoke the single tests.
70 * @return SimpleInvoker Individual test runner.
73 function &createInvoker() {
74 $invoker = &new SimpleErrorTrappingInvoker(new SimpleInvoker($this));
75 if (version_compare(phpversion(), '5') >= 0) {
76 $invoker = &new SimpleExceptionTrappingInvoker($invoker);
82 * Uses reflection to run every method within itself
83 * starting with the string "test" unless a method
85 * @param SimpleReporter $reporter Current test reporter.
88 function run(&$reporter) {
89 SimpleTest
::setCurrent($this);
90 $this->_reporter
= &$reporter;
91 $this->_reporter
->paintCaseStart($this->getLabel());
92 foreach ($this->getTests() as $method) {
93 if ($this->_reporter
->shouldInvoke($this->getLabel(), $method)) {
94 $invoker = &$this->_reporter
->createInvoker($this->createInvoker());
95 $invoker->before($method);
96 $invoker->invoke($method);
97 $invoker->after($method);
100 $this->_reporter
->paintCaseEnd($this->getLabel());
101 unset($this->_reporter
);
102 return $reporter->getStatus();
106 * Gets a list of test names. Normally that will
107 * be all internal methods that start with the
108 * name "test". This method should be overridden
109 * if you want a different rule.
110 * @return array List of test names.
113 function getTests() {
115 foreach (get_class_methods(get_class($this)) as $method) {
116 if ($this->_isTest($method)) {
117 $methods[] = $method;
124 * Tests to see if the method is a test that should
125 * be run. Currently any method that starts with 'test'
126 * is a candidate unless it is the constructor.
127 * @param string $method Method name to try.
128 * @return boolean True if test method.
131 function _isTest($method) {
132 if (strtolower(substr($method, 0, 4)) == 'test') {
133 return ! SimpleTestCompatibility
::isA($this, strtolower($method));
139 * Announces the start of the test.
140 * @param string $method Test method just started.
143 function before($method) {
144 $this->_reporter
->paintMethodStart($method);
145 $this->_observers
= array();
149 * Sets up unit test wide variables at the start
150 * of each test method. To be overridden in
151 * actual user test cases.
158 * Clears the data set in the setUp() method call.
159 * To be overridden by the user in actual user test cases.
162 function tearDown() {
166 * Announces the end of the test. Includes private clean up.
167 * @param string $method Test method just finished.
170 function after($method) {
171 for ($i = 0; $i < count($this->_observers
); $i++
) {
172 $this->_observers
[$i]->atTestEnd($method);
174 $this->_reporter
->paintMethodEnd($method);
178 * Sets up an observer for the test end.
179 * @param object $observer Must have atTestEnd()
183 function tell(&$observer) {
184 $this->_observers
[] = &$observer;
188 * Sends a pass event with a message.
189 * @param string $message Message to send.
192 function pass($message = "Pass") {
193 if (! isset($this->_reporter
)) {
194 trigger_error('Can only make assertions within test methods');
196 $this->_reporter
->paintPass(
197 $message . $this->getAssertionLine());
202 * Sends a fail event with a message.
203 * @param string $message Message to send.
206 function fail($message = "Fail") {
207 if (! isset($this->_reporter
)) {
208 trigger_error('Can only make assertions within test methods');
210 $this->_reporter
->paintFail(
211 $message . $this->getAssertionLine());
216 * Formats a PHP error and dispatches it to the
218 * @param integer $severity PHP error code.
219 * @param string $message Text of error.
220 * @param string $file File error occoured in.
221 * @param integer $line Line number of error.
224 function error($severity, $message, $file, $line) {
225 if (! isset($this->_reporter
)) {
226 trigger_error('Can only make assertions within test methods');
228 $this->_reporter
->paintError(
229 "Unexpected PHP error [$message] severity [$severity] in [$file] line [$line]");
233 * Formats an exception and dispatches it to the
235 * @param Exception $exception Object thrown.
238 function exception($exception) {
239 $this->_reporter
->paintError(
240 'Unexpected exception of type [' . get_class($exception) .
241 '] with message ['. $exception->getMessage() .
242 '] in ['. $exception->getFile() .
243 '] line [' . $exception->getLine() . ']');
247 * Sends a user defined event to the test reporter.
248 * This is for small scale extension where
249 * both the test case and either the reporter or
250 * display are subclassed.
251 * @param string $type Type of event.
252 * @param mixed $payload Object or message to deliver.
255 function signal($type, &$payload) {
256 if (! isset($this->_reporter
)) {
257 trigger_error('Can only make assertions within test methods');
259 $this->_reporter
->paintSignal($type, $payload);
263 * Cancels any outstanding errors.
266 function swallowErrors() {
267 $queue = &SimpleErrorQueue
::instance();
272 * Runs an expectation directly, for extending the
273 * tests with new expectation classes.
274 * @param SimpleExpectation $expectation Expectation subclass.
275 * @param mixed $compare Value to compare.
276 * @param string $message Message to display.
277 * @return boolean True on pass
280 function assert(&$expectation, $compare, $message = '%s') {
281 return $this->assertTrue(
282 $expectation->test($compare),
283 sprintf($message, $expectation->overlayMessage($compare)));
289 function assertExpectation(&$expectation, $compare, $message = '%s') {
290 return $this->assert($expectation, $compare, $message);
294 * Called from within the test methods to register
295 * passes and failures.
296 * @param boolean $result Pass on true.
297 * @param string $message Message to display describing
299 * @return boolean True on pass
302 function assertTrue($result, $message = false) {
304 $message = 'True assertion got ' . ($result ?
'True' : 'False');
307 return $this->pass($message);
309 return $this->fail($message);
314 * Will be true on false and vice versa. False
315 * is the PHP definition of false, so that null,
316 * empty strings, zero and an empty array all count
318 * @param boolean $result Pass on false.
319 * @param string $message Message to display.
320 * @return boolean True on pass
323 function assertFalse($result, $message = false) {
325 $message = 'False assertion got ' . ($result ?
'True' : 'False');
327 return $this->assertTrue(! $result, $message);
331 * Uses a stack trace to find the line of an assertion.
332 * @param string $format String formatting.
333 * @param array $stack Stack frames top most first. Only
334 * needed if not using the PHP
335 * backtrace function.
336 * @return string Line number of first assert*
337 * method embedded in format string.
340 function getAssertionLine($stack = false) {
341 if ($stack === false) {
342 $stack = SimpleTestCompatibility
::getStackTrace();
344 return SimpleDumper
::getFormattedAssertionLine($stack);
348 * Sends a formatted dump of a variable to the
349 * test suite for those emergency debugging
351 * @param mixed $variable Variable to display.
352 * @param string $message Message to display.
353 * @return mixed The original variable.
356 function dump($variable, $message = false) {
357 $formatted = SimpleDumper
::dump($variable);
359 $formatted = $message . "\n" . $formatted;
361 $this->_reporter
->paintFormattedMessage($formatted);
366 * Dispatches a text message straight to the
367 * test suite. Useful for status bar displays.
368 * @param string $message Message to show.
371 function sendMessage($message) {
372 $this->_reporter
->PaintMessage($message);
376 * Accessor for the number of subtests.
377 * @return integer Number of test cases.
387 * This is a composite test class for combining
388 * test cases and other RunnableTest classes into
390 * @package SimpleTest
391 * @subpackage UnitTester
396 var $_old_track_errors;
397 var $_xdebug_is_enabled;
400 * Sets the name of the test suite.
401 * @param string $label Name sent at the start and end
405 function GroupTest($label = false) {
406 $this->_label
= $label ?
$label : get_class($this);
407 $this->_test_cases
= array();
408 $this->_old_track_errors
= ini_get('track_errors');
409 $this->_xdebug_is_enabled
= function_exists('xdebug_is_enabled') ?
410 xdebug_is_enabled() : false;
414 * Accessor for the test name for subclasses.
415 * @return string Name of the test.
418 function getLabel() {
419 return $this->_label
;
423 * Adds a test into the suite. Can be either a group
424 * test or some other unit test.
425 * @param SimpleTestCase $test_case Suite or individual test
426 * case implementing the
427 * runnable test interface.
430 function addTestCase(&$test_case) {
431 $this->_test_cases
[] = &$test_case;
435 * Adds a test into the suite by class name. The class will
436 * be instantiated as needed.
437 * @param SimpleTestCase $test_case Suite or individual test
438 * case implementing the
439 * runnable test interface.
442 function addTestClass($class) {
443 if ($this->_getBaseTestCase($class) == 'grouptest') {
444 $this->_test_cases
[] = &new $class();
446 $this->_test_cases
[] = $class;
451 * Builds a group test from a library of test cases.
452 * The new group is composed into this one.
453 * @param string $test_file File name of library with
457 function addTestFile($test_file) {
458 $existing_classes = get_declared_classes();
459 if ($error = $this->_requireWithError($test_file)) {
460 $this->addTestCase(new BadGroupTest($test_file, $error));
463 $classes = $this->_selectRunnableTests($existing_classes, get_declared_classes());
464 if (count($classes) == 0) {
465 $this->addTestCase(new BadGroupTest($test_file, "No runnable test cases in [$test_file]"));
468 $group = &$this->_createGroupFromClasses($test_file, $classes);
469 $this->addTestCase($group);
473 * Requires a source file recording any syntax errors.
474 * @param string $file File name to require in.
475 * @return string/boolean An error message on failure or false
479 function _requireWithError($file) {
480 $this->_enableErrorReporting();
482 $error = isset($php_errormsg) ?
$php_errormsg : false;
483 $this->_disableErrorReporting();
484 $self_inflicted_errors = array(
485 'Assigning the return value of new by reference is deprecated',
486 'var: Deprecated. Please use the public/private/protected modifiers');
487 if (in_array($error, $self_inflicted_errors)) {
494 * Sets up detection of parse errors. Note that XDebug
495 * interferes with this and has to be disabled. This is
496 * to make sure the correct error code is returned
497 * from unattended scripts.
500 function _enableErrorReporting() {
501 if ($this->_xdebug_is_enabled
) {
504 ini_set('track_errors', true);
508 * Resets detection of parse errors to their old values.
509 * This is to make sure the correct error code is returned
510 * from unattended scripts.
513 function _disableErrorReporting() {
514 ini_set('track_errors', $this->_old_track_errors
);
515 if ($this->_xdebug_is_enabled
) {
521 * Calculates the incoming test cases from a before
522 * and after list of loaded classes. Skips abstract
524 * @param array $existing_classes Classes before require().
525 * @param array $new_classes Classes after require().
526 * @return array New classes which are test
527 * cases that shouldn't be ignored.
530 function _selectRunnableTests($existing_classes, $new_classes) {
532 foreach ($new_classes as $class) {
533 if (in_array($class, $existing_classes)) {
536 if ($this->_getBaseTestCase($class)) {
537 $reflection = new SimpleReflection($class);
538 if ($reflection->isAbstract()) {
539 SimpleTest
::ignore($class);
548 * Builds a group test from a class list.
549 * @param string $title Title of new group.
550 * @param array $classes Test classes.
551 * @return GroupTest Group loaded with the new
555 function &_createGroupFromClasses($title, $classes) {
556 SimpleTest
::ignoreParentsIfIgnored($classes);
557 $group = &new GroupTest($title);
558 foreach ($classes as $class) {
559 if (! SimpleTest
::isIgnored($class)) {
560 $group->addTestClass($class);
567 * Test to see if a class is derived from the
568 * SimpleTestCase class.
569 * @param string $class Class name.
572 function _getBaseTestCase($class) {
573 while ($class = get_parent_class($class)) {
574 $class = strtolower($class);
575 if ($class == "simpletestcase" ||
$class == "grouptest") {
583 * Delegates to a visiting collector to add test
585 * @param string $path Path to scan from.
586 * @param SimpleCollector $collector Directory scanner.
589 function collect($path, &$collector) {
590 $collector->collect($this, $path);
594 * Invokes run() on all of the held test cases, instantiating
596 * @param SimpleReporter $reporter Current test reporter.
599 function run(&$reporter) {
600 $reporter->paintGroupStart($this->getLabel(), $this->getSize());
601 for ($i = 0, $count = count($this->_test_cases
); $i < $count; $i++
) {
602 if (is_string($this->_test_cases
[$i])) {
603 $class = $this->_test_cases
[$i];
604 $test = &new $class();
605 $test->run($reporter);
607 $this->_test_cases
[$i]->run($reporter);
610 $reporter->paintGroupEnd($this->getLabel());
611 return $reporter->getStatus();
615 * Number of contained test cases.
616 * @return integer Total count of cases in the group.
621 foreach ($this->_test_cases
as $case) {
622 if (is_string($case)) {
625 $count +
= $case->getSize();
633 * This is a failing group test for when a test suite hasn't
635 * @package SimpleTest
636 * @subpackage UnitTester
643 * Sets the name of the test suite and error message.
644 * @param string $label Name sent at the start and end
648 function BadGroupTest($label, $error) {
649 $this->_label
= $label;
650 $this->_error
= $error;
654 * Accessor for the test name for subclasses.
655 * @return string Name of the test.
658 function getLabel() {
659 return $this->_label
;
663 * Sends a single error to the reporter.
664 * @param SimpleReporter $reporter Current test reporter.
667 function run(&$reporter) {
668 $reporter->paintGroupStart($this->getLabel(), $this->getSize());
669 $reporter->paintFail('Bad GroupTest [' . $this->getLabel() .
670 '] with error [' . $this->_error
. ']');
671 $reporter->paintGroupEnd($this->getLabel());
672 return $reporter->getStatus();
676 * Number of contained test cases. Always zero.
677 * @return integer Total count of cases in the group.