adding some strings
[moodle-linuxchix.git] / lib / simpletestlib / test_case.php
blob91835a31096f263d33dcb82d5fdfc7845e167f42
1 <?php
2 /**
3 * Base include file for SimpleTest
4 * @package SimpleTest
5 * @subpackage UnitTester
6 * @version $Id$
7 */
9 /**#@+
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');
23 } else {
24 require_once(dirname(__FILE__) . '/reflection_php4.php');
26 if (! defined('SIMPLE_TEST')) {
27 /** @ignore */
28 define('SIMPLE_TEST', dirname(__FILE__) . DIRECTORY_SEPARATOR);
30 /**#@-*/
32 /**
33 * Basic test case. This is the smallest unit of a test
34 * suite. It searches for
35 * all methods that start with the the string "test" and
36 * runs them. Working test cases extend this class.
37 * @package SimpleTest
38 * @subpackage UnitTester
40 class SimpleTestCase {
41 var $_label = false;
42 var $_reporter;
43 var $_observers;
44 var $_should_skip = false;
46 /**
47 * Sets up the test with no display.
48 * @param string $label If no test name is given then
49 * the class name is used.
50 * @access public
52 function SimpleTestCase($label = false) {
53 if ($label) {
54 $this->_label = $label;
58 /**
59 * Accessor for the test name for subclasses.
60 * @return string Name of the test.
61 * @access public
63 function getLabel() {
64 return $this->_label ? $this->_label : get_class($this);
67 /**
68 * This is a placeholder for skipping tests. In this
69 * method you place skipIf() and skipUnless() calls to
70 * set the skipping state.
71 * @access public
73 function skip() {
76 /**
77 * Will issue a message to the reporter and tell the test
78 * case to skip if the incoming flag is true.
79 * @param string $should_skip Condition causing the tests to be skipped.
80 * @param string $message Text of skip condition.
81 * @access public
83 function skipIf($should_skip, $message = '%s') {
84 if ($should_skip && ! $this->_should_skip) {
85 $this->_should_skip = true;
86 $message = sprintf($message, 'Skipping [' . get_class($this) . ']');
87 $this->_reporter->paintSkip($message . $this->getAssertionLine());
91 /**
92 * Will issue a message to the reporter and tell the test
93 * case to skip if the incoming flag is false.
94 * @param string $shouldnt_skip Condition causing the tests to be run.
95 * @param string $message Text of skip condition.
96 * @access public
98 function skipUnless($shouldnt_skip, $message = false) {
99 $this->skipIf(! $shouldnt_skip, $message);
103 * Used to invoke the single tests.
104 * @return SimpleInvoker Individual test runner.
105 * @access public
107 function &createInvoker() {
108 $invoker = &new SimpleErrorTrappingInvoker(new SimpleInvoker($this));
109 if (version_compare(phpversion(), '5') >= 0) {
110 $invoker = &new SimpleExceptionTrappingInvoker($invoker);
112 return $invoker;
116 * Uses reflection to run every method within itself
117 * starting with the string "test" unless a method
118 * is specified.
119 * @param SimpleReporter $reporter Current test reporter.
120 * @return boolean True if all tests passed.
121 * @access public
123 function run(&$reporter) {
124 $context = &SimpleTest::getContext();
125 $context->setTest($this);
126 $context->setReporter($reporter);
127 $this->_reporter = &$reporter;
128 $reporter->paintCaseStart($this->getLabel());
129 $this->skip();
130 if (! $this->_should_skip) {
131 foreach ($this->getTests() as $method) {
132 if ($reporter->shouldInvoke($this->getLabel(), $method)) {
133 $invoker = &$this->_reporter->createInvoker($this->createInvoker());
134 $invoker->before($method);
135 $invoker->invoke($method);
136 $invoker->after($method);
140 $reporter->paintCaseEnd($this->getLabel());
141 unset($this->_reporter);
142 return $reporter->getStatus();
146 * Gets a list of test names. Normally that will
147 * be all internal methods that start with the
148 * name "test". This method should be overridden
149 * if you want a different rule.
150 * @return array List of test names.
151 * @access public
153 function getTests() {
154 $methods = array();
155 foreach (get_class_methods(get_class($this)) as $method) {
156 if ($this->_isTest($method)) {
157 $methods[] = $method;
160 return $methods;
164 * Tests to see if the method is a test that should
165 * be run. Currently any method that starts with 'test'
166 * is a candidate unless it is the constructor.
167 * @param string $method Method name to try.
168 * @return boolean True if test method.
169 * @access protected
171 function _isTest($method) {
172 if (strtolower(substr($method, 0, 4)) == 'test') {
173 return ! SimpleTestCompatibility::isA($this, strtolower($method));
175 return false;
179 * Announces the start of the test.
180 * @param string $method Test method just started.
181 * @access public
183 function before($method) {
184 $this->_reporter->paintMethodStart($method);
185 $this->_observers = array();
189 * Sets up unit test wide variables at the start
190 * of each test method. To be overridden in
191 * actual user test cases.
192 * @access public
194 function setUp() {
198 * Clears the data set in the setUp() method call.
199 * To be overridden by the user in actual user test cases.
200 * @access public
202 function tearDown() {
206 * Announces the end of the test. Includes private clean up.
207 * @param string $method Test method just finished.
208 * @access public
210 function after($method) {
211 for ($i = 0; $i < count($this->_observers); $i++) {
212 $this->_observers[$i]->atTestEnd($method, $this);
214 $this->_reporter->paintMethodEnd($method);
218 * Sets up an observer for the test end.
219 * @param object $observer Must have atTestEnd()
220 * method.
221 * @access public
223 function tell(&$observer) {
224 $this->_observers[] = &$observer;
228 * @deprecated
230 function pass($message = "Pass") {
231 if (! isset($this->_reporter)) {
232 trigger_error('Can only make assertions within test methods');
234 $this->_reporter->paintPass(
235 $message . $this->getAssertionLine());
236 return true;
240 * Sends a fail event with a message.
241 * @param string $message Message to send.
242 * @access public
244 function fail($message = "Fail") {
245 if (! isset($this->_reporter)) {
246 trigger_error('Can only make assertions within test methods');
248 $this->_reporter->paintFail(
249 $message . $this->getAssertionLine());
250 return false;
254 * Formats a PHP error and dispatches it to the
255 * reporter.
256 * @param integer $severity PHP error code.
257 * @param string $message Text of error.
258 * @param string $file File error occoured in.
259 * @param integer $line Line number of error.
260 * @access public
262 function error($severity, $message, $file, $line) {
263 if (! isset($this->_reporter)) {
264 trigger_error('Can only make assertions within test methods');
266 $this->_reporter->paintError(
267 "Unexpected PHP error [$message] severity [$severity] in [$file line $line]");
271 * Formats an exception and dispatches it to the
272 * reporter.
273 * @param Exception $exception Object thrown.
274 * @access public
276 function exception($exception) {
277 $this->_reporter->paintException($exception);
281 * @deprecated
283 function signal($type, &$payload) {
284 if (! isset($this->_reporter)) {
285 trigger_error('Can only make assertions within test methods');
287 $this->_reporter->paintSignal($type, $payload);
291 * Runs an expectation directly, for extending the
292 * tests with new expectation classes.
293 * @param SimpleExpectation $expectation Expectation subclass.
294 * @param mixed $compare Value to compare.
295 * @param string $message Message to display.
296 * @return boolean True on pass
297 * @access public
299 function assert(&$expectation, $compare, $message = '%s') {
300 if ($expectation->test($compare)) {
301 return $this->pass(sprintf(
302 $message,
303 $expectation->overlayMessage($compare, $this->_reporter->getDumper())));
304 } else {
305 return $this->fail(sprintf(
306 $message,
307 $expectation->overlayMessage($compare, $this->_reporter->getDumper())));
312 * @deprecated
314 function assertExpectation(&$expectation, $compare, $message = '%s') {
315 return $this->assert($expectation, $compare, $message);
319 * Uses a stack trace to find the line of an assertion.
320 * @return string Line number of first assert*
321 * method embedded in format string.
322 * @access public
324 function getAssertionLine() {
325 $trace = new SimpleStackTrace(array('assert', 'expect', 'pass', 'fail', 'skip'));
326 return $trace->traceMethod();
330 * Sends a formatted dump of a variable to the
331 * test suite for those emergency debugging
332 * situations.
333 * @param mixed $variable Variable to display.
334 * @param string $message Message to display.
335 * @return mixed The original variable.
336 * @access public
338 function dump($variable, $message = false) {
339 $dumper = $this->_reporter->getDumper();
340 $formatted = $dumper->dump($variable);
341 if ($message) {
342 $formatted = $message . "\n" . $formatted;
344 $this->_reporter->paintFormattedMessage($formatted);
345 return $variable;
349 * @deprecated
351 function sendMessage($message) {
352 $this->_reporter->PaintMessage($message);
356 * Accessor for the number of subtests.
357 * @return integer Number of test cases.
358 * @access public
359 * @static
361 function getSize() {
362 return 1;
367 * This is a composite test class for combining
368 * test cases and other RunnableTest classes into
369 * a group test.
370 * @package SimpleTest
371 * @subpackage UnitTester
373 class TestSuite {
374 var $_label;
375 var $_test_cases;
376 var $_old_track_errors;
377 var $_xdebug_is_enabled;
380 * Sets the name of the test suite.
381 * @param string $label Name sent at the start and end
382 * of the test.
383 * @access public
385 function TestSuite($label = false) {
386 $this->_label = $label ? $label : get_class($this);
387 $this->_test_cases = array();
388 $this->_old_track_errors = ini_get('track_errors');
389 $this->_xdebug_is_enabled = function_exists('xdebug_is_enabled') ?
390 xdebug_is_enabled() : false;
394 * Accessor for the test name for subclasses.
395 * @return string Name of the test.
396 * @access public
398 function getLabel() {
399 return $this->_label;
403 * Adds a test into the suite. Can be either a group
404 * test or some other unit test.
405 * @param SimpleTestCase $test_case Suite or individual test
406 * case implementing the
407 * runnable test interface.
408 * @access public
410 function addTestCase(&$test_case) {
411 $this->_test_cases[] = &$test_case;
415 * Adds a test into the suite by class name. The class will
416 * be instantiated as needed.
417 * @param SimpleTestCase $test_case Suite or individual test
418 * case implementing the
419 * runnable test interface.
420 * @access public
422 function addTestClass($class) {
423 if ($this->_getBaseTestCase($class) == 'testsuite' || $this->_getBaseTestCase($class) == 'grouptest') {
424 $this->_test_cases[] = &new $class();
425 } else {
426 $this->_test_cases[] = $class;
431 * Builds a group test from a library of test cases.
432 * The new group is composed into this one.
433 * @param string $test_file File name of library with
434 * test case classes.
435 * @access public
437 function addTestFile($test_file) {
438 $existing_classes = get_declared_classes();
439 if ($error = $this->_requireWithError($test_file)) {
440 $this->addTestCase(new BadTestSuite($test_file, $error));
441 return;
443 $classes = $this->_selectRunnableTests($existing_classes, get_declared_classes());
444 if (count($classes) == 0) {
445 $this->addTestCase(new BadTestSuite($test_file, "No runnable test cases in [$test_file]"));
446 return;
448 $group = &$this->_createGroupFromClasses($test_file, $classes);
449 $this->addTestCase($group);
453 * Requires a source file recording any syntax errors.
454 * @param string $file File name to require in.
455 * @return string/boolean An error message on failure or false
456 * if no errors.
457 * @access private
459 function _requireWithError($file) {
460 $this->_enableErrorReporting();
461 global $CFG; // Moodle patch for $CFG global in unit test files
462 include($file);
463 $error = isset($php_errormsg) ? $php_errormsg : false;
464 $this->_disableErrorReporting();
465 $self_inflicted_errors = array(
466 '/Assigning the return value of new by reference/i',
467 '/var: Deprecated/i',
468 '/Non-static method/i');
469 foreach ($self_inflicted_errors as $pattern) {
470 if (preg_match($pattern, $error)) {
471 return false;
474 return $error;
478 * Sets up detection of parse errors. Note that XDebug
479 * interferes with this and has to be disabled. This is
480 * to make sure the correct error code is returned
481 * from unattended scripts.
482 * @access private
484 function _enableErrorReporting() {
485 if ($this->_xdebug_is_enabled) {
486 xdebug_disable();
488 ini_set('track_errors', true);
492 * Resets detection of parse errors to their old values.
493 * This is to make sure the correct error code is returned
494 * from unattended scripts.
495 * @access private
497 function _disableErrorReporting() {
498 ini_set('track_errors', $this->_old_track_errors);
499 if ($this->_xdebug_is_enabled) {
500 xdebug_enable();
505 * Calculates the incoming test cases from a before
506 * and after list of loaded classes. Skips abstract
507 * classes.
508 * @param array $existing_classes Classes before require().
509 * @param array $new_classes Classes after require().
510 * @return array New classes which are test
511 * cases that shouldn't be ignored.
512 * @access private
514 function _selectRunnableTests($existing_classes, $new_classes) {
515 $classes = array();
516 foreach ($new_classes as $class) {
517 if (in_array($class, $existing_classes)) {
518 continue;
520 if ($this->_getBaseTestCase($class)) {
521 $reflection = new SimpleReflection($class);
522 if ($reflection->isAbstract()) {
523 SimpleTest::ignore($class);
525 $classes[] = $class;
528 return $classes;
532 * Builds a group test from a class list.
533 * @param string $title Title of new group.
534 * @param array $classes Test classes.
535 * @return TestSuite Group loaded with the new
536 * test cases.
537 * @access private
539 function &_createGroupFromClasses($title, $classes) {
540 SimpleTest::ignoreParentsIfIgnored($classes);
541 $group = &new TestSuite($title);
542 foreach ($classes as $class) {
543 if (! SimpleTest::isIgnored($class)) {
544 $group->addTestClass($class);
547 return $group;
551 * Test to see if a class is derived from the
552 * SimpleTestCase class.
553 * @param string $class Class name.
554 * @access private
556 function _getBaseTestCase($class) {
557 while ($class = get_parent_class($class)) {
558 $class = strtolower($class);
559 if ($class == 'simpletestcase' || $class == 'testsuite' || $class == 'grouptest') {
560 return $class;
563 return false;
567 * Delegates to a visiting collector to add test
568 * files.
569 * @param string $path Path to scan from.
570 * @param SimpleCollector $collector Directory scanner.
571 * @access public
573 function collect($path, &$collector) {
574 $collector->collect($this, $path);
578 * Invokes run() on all of the held test cases, instantiating
579 * them if necessary.
580 * @param SimpleReporter $reporter Current test reporter.
581 * @access public
583 function run(&$reporter) {
584 $reporter->paintGroupStart($this->getLabel(), $this->getSize());
585 for ($i = 0, $count = count($this->_test_cases); $i < $count; $i++) {
586 if (is_string($this->_test_cases[$i])) {
587 $class = $this->_test_cases[$i];
588 $test = &new $class();
589 $test->run($reporter);
590 unset($test);
591 } else {
592 $this->_test_cases[$i]->run($reporter);
595 $reporter->paintGroupEnd($this->getLabel());
596 return $reporter->getStatus();
600 * Number of contained test cases.
601 * @return integer Total count of cases in the group.
602 * @access public
604 function getSize() {
605 $count = 0;
606 foreach ($this->_test_cases as $case) {
607 if (is_string($case)) {
608 $count++;
609 } else {
610 $count += $case->getSize();
613 return $count;
618 * @deprecated
620 class GroupTest extends TestSuite { }
623 * This is a failing group test for when a test suite hasn't
624 * loaded properly.
625 * @package SimpleTest
626 * @subpackage UnitTester
628 class BadTestSuite {
629 var $_label;
630 var $_error;
633 * Sets the name of the test suite and error message.
634 * @param string $label Name sent at the start and end
635 * of the test.
636 * @access public
638 function BadTestSuite($label, $error) {
639 $this->_label = $label;
640 $this->_error = $error;
644 * Accessor for the test name for subclasses.
645 * @return string Name of the test.
646 * @access public
648 function getLabel() {
649 return $this->_label;
653 * Sends a single error to the reporter.
654 * @param SimpleReporter $reporter Current test reporter.
655 * @access public
657 function run(&$reporter) {
658 $reporter->paintGroupStart($this->getLabel(), $this->getSize());
659 $reporter->paintFail('Bad TestSuite [' . $this->getLabel() .
660 '] with error [' . $this->_error . ']');
661 $reporter->paintGroupEnd($this->getLabel());
662 return $reporter->getStatus();
666 * Number of contained test cases. Always zero.
667 * @return integer Total count of cases in the group.
668 * @access public
670 function getSize() {
671 return 0;
676 * @deprecated
678 class BadGroupTest extends BadTestSuite { }