[ Index ]

PHP Cross Reference of Moodle 1.9.3 [Build 15-Oct-2008]

title

Body

[close]

/lib/simpletestlib/ -> test_case.php (source)

   1  <?php
   2      /**
   3       *    Base include file for SimpleTest
   4       *    @package    SimpleTest
   5       *    @subpackage    UnitTester
   6       *    @version    $Id$
   7       */
   8  
   9      /**#@+
  10       * Includes SimpleTest files and defined the root constant
  11       * for dependent libraries.
  12       */
  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');
  25      }
  26      if (! defined('SIMPLE_TEST')) {
  27          /** @ignore */
  28          define('SIMPLE_TEST', dirname(__FILE__) . DIRECTORY_SEPARATOR);
  29      }
  30      /**#@-*/
  31  
  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
  39       */
  40      class SimpleTestCase {
  41          var $_label = false;
  42          var $_reporter;
  43          var $_observers;
  44          var $_should_skip = false;
  45  
  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
  51           */
  52          function SimpleTestCase($label = false) {
  53              if ($label) {
  54                  $this->_label = $label;
  55              }
  56          }
  57  
  58          /**
  59           *    Accessor for the test name for subclasses.
  60           *    @return string           Name of the test.
  61           *    @access public
  62           */
  63          function getLabel() {
  64              return $this->_label ? $this->_label : get_class($this);
  65          }
  66  
  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
  72           */
  73          function skip() {
  74          }
  75          
  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
  82           */
  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());
  88              }
  89          }
  90          
  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
  97           */
  98          function skipUnless($shouldnt_skip, $message = false) {
  99              $this->skipIf(! $shouldnt_skip, $message);
 100          }
 101          
 102          /**
 103           *    Used to invoke the single tests.
 104           *    @return SimpleInvoker        Individual test runner.
 105           *    @access public
 106           */
 107          function &createInvoker() {
 108              $invoker = &new SimpleErrorTrappingInvoker(new SimpleInvoker($this));
 109              if (version_compare(phpversion(), '5') >= 0) {
 110                  $invoker = &new SimpleExceptionTrappingInvoker($invoker);
 111              }
 112              return $invoker;
 113          }
 114  
 115          /**
 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
 122           */
 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);
 137                      }
 138                  }
 139              }
 140              $reporter->paintCaseEnd($this->getLabel());
 141              unset($this->_reporter);
 142              return $reporter->getStatus();
 143          }
 144  
 145          /**
 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
 152           */
 153          function getTests() {
 154              $methods = array();
 155              foreach (get_class_methods(get_class($this)) as $method) {
 156                  if ($this->_isTest($method)) {
 157                      $methods[] = $method;
 158                  }
 159              }
 160              return $methods;
 161          }
 162  
 163          /**
 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
 170           */
 171          function _isTest($method) {
 172              if (strtolower(substr($method, 0, 4)) == 'test') {
 173                  return ! SimpleTestCompatibility::isA($this, strtolower($method));
 174              }
 175              return false;
 176          }
 177  
 178          /**
 179           *    Announces the start of the test.
 180           *    @param string $method    Test method just started.
 181           *    @access public
 182           */
 183          function before($method) {
 184              $this->_reporter->paintMethodStart($method);
 185              $this->_observers = array();
 186          }
 187  
 188          /**
 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
 193           */
 194          function setUp() {
 195          }
 196  
 197          /**
 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
 201           */
 202          function tearDown() {
 203          }
 204  
 205          /**
 206           *    Announces the end of the test. Includes private clean up.
 207           *    @param string $method    Test method just finished.
 208           *    @access public
 209           */
 210          function after($method) {
 211              for ($i = 0; $i < count($this->_observers); $i++) {
 212                  $this->_observers[$i]->atTestEnd($method, $this);
 213              }
 214              $this->_reporter->paintMethodEnd($method);
 215          }
 216  
 217          /**
 218           *    Sets up an observer for the test end.
 219           *    @param object $observer    Must have atTestEnd()
 220           *                               method.
 221           *    @access public
 222           */
 223          function tell(&$observer) {
 224              $this->_observers[] = &$observer;
 225          }
 226  
 227          /**
 228           *    @deprecated
 229           */
 230          function pass($message = "Pass") {
 231              if (! isset($this->_reporter)) {
 232                  trigger_error('Can only make assertions within test methods');
 233              }
 234              $this->_reporter->paintPass(
 235                      $message . $this->getAssertionLine());
 236              return true;
 237          }
 238  
 239          /**
 240           *    Sends a fail event with a message.
 241           *    @param string $message        Message to send.
 242           *    @access public
 243           */
 244          function fail($message = "Fail") {
 245              if (! isset($this->_reporter)) {
 246                  trigger_error('Can only make assertions within test methods');
 247              }
 248              $this->_reporter->paintFail(
 249                      $message . $this->getAssertionLine());
 250              return false;
 251          }
 252  
 253          /**
 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
 261           */
 262          function error($severity, $message, $file, $line) {
 263              if (! isset($this->_reporter)) {
 264                  trigger_error('Can only make assertions within test methods');
 265              }
 266              $this->_reporter->paintError(
 267                      "Unexpected PHP error [$message] severity [$severity] in [$file line $line]");
 268          }
 269  
 270          /**
 271           *    Formats an exception and dispatches it to the
 272           *    reporter.
 273           *    @param Exception $exception    Object thrown.
 274           *    @access public
 275           */
 276          function exception($exception) {
 277              $this->_reporter->paintException($exception);
 278          }
 279  
 280          /**
 281           *    @deprecated
 282           */
 283          function signal($type, &$payload) {
 284              if (! isset($this->_reporter)) {
 285                  trigger_error('Can only make assertions within test methods');
 286              }
 287              $this->_reporter->paintSignal($type, $payload);
 288          }
 289  
 290          /**
 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
 298           */
 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())));
 308              }
 309          }
 310  
 311          /**
 312           *      @deprecated
 313           */
 314          function assertExpectation(&$expectation, $compare, $message = '%s') {
 315              return $this->assert($expectation, $compare, $message);
 316          }
 317  
 318          /**
 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
 323           */
 324          function getAssertionLine() {
 325              $trace = new SimpleStackTrace(array('assert', 'expect', 'pass', 'fail', 'skip'));
 326              return $trace->traceMethod();
 327          }
 328  
 329          /**
 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
 337           */
 338          function dump($variable, $message = false) {
 339              $dumper = $this->_reporter->getDumper();
 340              $formatted = $dumper->dump($variable);
 341              if ($message) {
 342                  $formatted = $message . "\n" . $formatted;
 343              }
 344              $this->_reporter->paintFormattedMessage($formatted);
 345              return $variable;
 346          }
 347  
 348          /**
 349           *    @deprecated
 350           */
 351          function sendMessage($message) {
 352              $this->_reporter->PaintMessage($message);
 353          }
 354  
 355          /**
 356           *    Accessor for the number of subtests.
 357           *    @return integer           Number of test cases.
 358           *    @access public
 359           *    @static
 360           */
 361          function getSize() {
 362              return 1;
 363          }
 364      }
 365  
 366      /**
 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
 372       */
 373      class TestSuite {
 374          var $_label;
 375          var $_test_cases;
 376          var $_old_track_errors;
 377          var $_xdebug_is_enabled;
 378  
 379          /**
 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
 384           */
 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;
 391          }
 392  
 393          /**
 394           *    Accessor for the test name for subclasses.
 395           *    @return string           Name of the test.
 396           *    @access public
 397           */
 398          function getLabel() {
 399              return $this->_label;
 400          }
 401  
 402          /**
 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
 409           */
 410          function addTestCase(&$test_case) {
 411              $this->_test_cases[] = &$test_case;
 412          }
 413  
 414          /**
 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
 421           */
 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;
 427              }
 428          }
 429  
 430          /**
 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
 436           */
 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;
 442              }
 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;
 447              }
 448              $group = &$this->_createGroupFromClasses($test_file, $classes);
 449              $this->addTestCase($group);
 450          }
 451  
 452          /**
 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
 458           */
 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;
 472                  }
 473              }
 474              return $error;
 475          }
 476  
 477          /**
 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
 483           */
 484          function _enableErrorReporting() {
 485              if ($this->_xdebug_is_enabled) {
 486                  xdebug_disable();
 487              }
 488              ini_set('track_errors', true);
 489          }
 490  
 491          /**
 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
 496           */
 497          function _disableErrorReporting() {
 498              ini_set('track_errors', $this->_old_track_errors);
 499              if ($this->_xdebug_is_enabled) {
 500                  xdebug_enable();
 501              }
 502          }
 503  
 504          /**
 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
 513           */
 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;
 519                  }
 520                  if ($this->_getBaseTestCase($class)) {
 521                      $reflection = new SimpleReflection($class);
 522                      if ($reflection->isAbstract()) {
 523                          SimpleTest::ignore($class);
 524                      }
 525                      $classes[] = $class;
 526                  }
 527              }
 528              return $classes;
 529          }
 530  
 531          /**
 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
 538           */
 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);
 545                  }
 546              }
 547              return $group;
 548          }
 549  
 550          /**
 551           *    Test to see if a class is derived from the
 552           *    SimpleTestCase class.
 553           *    @param string $class     Class name.
 554           *    @access private
 555           */
 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;
 561                  }
 562              }
 563              return false;
 564          }
 565  
 566          /**
 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
 572           */
 573          function collect($path, &$collector) {
 574              $collector->collect($this, $path);
 575          }
 576  
 577          /**
 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
 582           */
 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);
 593                  }
 594              }
 595              $reporter->paintGroupEnd($this->getLabel());
 596              return $reporter->getStatus();
 597          }
 598  
 599          /**
 600           *    Number of contained test cases.
 601           *    @return integer     Total count of cases in the group.
 602           *    @access public
 603           */
 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();
 611                  }
 612              }
 613              return $count;
 614          }
 615      }
 616      
 617      /**
 618       *    @deprecated
 619       */
 620      class GroupTest extends TestSuite { }
 621  
 622      /**
 623       *    This is a failing group test for when a test suite hasn't
 624       *    loaded properly.
 625       *    @package        SimpleTest
 626       *    @subpackage    UnitTester
 627       */
 628      class BadTestSuite {
 629          var $_label;
 630          var $_error;
 631  
 632          /**
 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
 637           */
 638          function BadTestSuite($label, $error) {
 639              $this->_label = $label;
 640              $this->_error = $error;
 641          }
 642  
 643          /**
 644           *    Accessor for the test name for subclasses.
 645           *    @return string           Name of the test.
 646           *    @access public
 647           */
 648          function getLabel() {
 649              return $this->_label;
 650          }
 651  
 652          /**
 653           *    Sends a single error to the reporter.
 654           *    @param SimpleReporter $reporter    Current test reporter.
 655           *    @access public
 656           */
 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();
 663          }
 664  
 665          /**
 666           *    Number of contained test cases. Always zero.
 667           *    @return integer     Total count of cases in the group.
 668           *    @access public
 669           */
 670          function getSize() {
 671              return 0;
 672          }
 673      }
 674      
 675      /**
 676       *    @deprecated
 677       */
 678      class BadGroupTest extends BadTestSuite { }
 679  ?>


Generated: Wed Jan 14 11:33:29 2009 Cross-referenced by PHPXref 0.7