PageRenderTime 46ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/common/libraries/plugin/simpletest/test_case.php

https://bitbucket.org/renaatdemuynck/chamilo
PHP | 806 lines | 435 code | 52 blank | 319 comment | 41 complexity | 2dc2185c8fc8ab9cc6653f02af7ad1bd MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1, LGPL-3.0, GPL-3.0, MIT, GPL-2.0
  1. <?php
  2. /**
  3. * Base include file for SimpleTest
  4. * @package SimpleTest
  5. * @subpackage UnitTester
  6. * @version $Id: test_case.php 1726 2008-04-08 01:20:10Z lastcraft $
  7. */
  8. /**#@+
  9. * Includes SimpleTest files and defined the root constant
  10. * for dependent libraries.
  11. */
  12. require_once (dirname(__FILE__) . '/invoker.php');
  13. require_once (dirname(__FILE__) . '/errors.php');
  14. require_once (dirname(__FILE__) . '/compatibility.php');
  15. require_once (dirname(__FILE__) . '/scorer.php');
  16. require_once (dirname(__FILE__) . '/expectation.php');
  17. require_once (dirname(__FILE__) . '/dumper.php');
  18. require_once (dirname(__FILE__) . '/simpletest.php');
  19. if (version_compare(phpversion(), '5') >= 0)
  20. {
  21. require_once (dirname(__FILE__) . '/exceptions.php');
  22. require_once (dirname(__FILE__) . '/reflection_php5.php');
  23. }
  24. else
  25. {
  26. require_once (dirname(__FILE__) . '/reflection_php4.php');
  27. }
  28. if (! defined('SIMPLE_TEST'))
  29. {
  30. /**
  31. * @ignore
  32. */
  33. define('SIMPLE_TEST', dirname(__FILE__) . DIRECTORY_SEPARATOR);
  34. }
  35. /**#@-*/
  36. /**
  37. * Basic test case. This is the smallest unit of a test
  38. * suite. It searches for
  39. * all methods that start with the the string "test" and
  40. * runs them. Working test cases extend this class.
  41. * @package SimpleTest
  42. * @subpackage UnitTester
  43. */
  44. class SimpleTestCase
  45. {
  46. var $_label = false;
  47. var $_reporter;
  48. var $_observers;
  49. var $_should_skip = false;
  50. /**
  51. * Sets up the test with no display.
  52. * @param string $label If no test name is given then
  53. * the class name is used.
  54. * @access public
  55. */
  56. function __construct($label = false)
  57. {
  58. if ($label)
  59. {
  60. $this->_label = $label;
  61. }
  62. }
  63. /**
  64. * Accessor for the test name for subclasses.
  65. * @return string Name of the test.
  66. * @access public
  67. */
  68. function getLabel()
  69. {
  70. return $this->_label ? $this->_label : get_class($this);
  71. }
  72. /**
  73. * This is a placeholder for skipping tests. In this
  74. * method you place skipIf() and skipUnless() calls to
  75. * set the skipping state.
  76. * @access public
  77. */
  78. function skip()
  79. {
  80. }
  81. /**
  82. * Will issue a message to the reporter and tell the test
  83. * case to skip if the incoming flag is true.
  84. * @param string $should_skip Condition causing the tests to be skipped.
  85. * @param string $message Text of skip condition.
  86. * @access public
  87. */
  88. function skipIf($should_skip, $message = '%s')
  89. {
  90. if ($should_skip && ! $this->_should_skip)
  91. {
  92. $this->_should_skip = true;
  93. $message = sprintf($message, 'Skipping [' . get_class($this) . ']');
  94. $this->_reporter->paintSkip($message . $this->getAssertionLine());
  95. }
  96. }
  97. /**
  98. * Will issue a message to the reporter and tell the test
  99. * case to skip if the incoming flag is false.
  100. * @param string $shouldnt_skip Condition causing the tests to be run.
  101. * @param string $message Text of skip condition.
  102. * @access public
  103. */
  104. function skipUnless($shouldnt_skip, $message = false)
  105. {
  106. $this->skipIf(! $shouldnt_skip, $message);
  107. }
  108. /**
  109. * Used to invoke the single tests.
  110. * @return SimpleInvoker Individual test runner.
  111. * @access public
  112. */
  113. function &createInvoker()
  114. {
  115. $invoker = &new SimpleErrorTrappingInvoker(new SimpleInvoker($this));
  116. if (version_compare(phpversion(), '5') >= 0)
  117. {
  118. $invoker = &new SimpleExceptionTrappingInvoker($invoker);
  119. }
  120. return $invoker;
  121. }
  122. /**
  123. * Uses reflection to run every method within itself
  124. * starting with the string "test" unless a method
  125. * is specified.
  126. * @param SimpleReporter $reporter Current test reporter.
  127. * @return boolean True if all tests passed.
  128. * @access public
  129. */
  130. function run(&$reporter)
  131. {
  132. $context = &SimpleTest :: getContext();
  133. $context->setTest($this);
  134. $context->setReporter($reporter);
  135. $this->_reporter = &$reporter;
  136. $started = false;
  137. foreach ($this->getTests() as $method)
  138. {
  139. if ($reporter->shouldInvoke($this->getLabel(), $method))
  140. {
  141. $this->skip();
  142. if ($this->_should_skip)
  143. {
  144. break;
  145. }
  146. if (! $started)
  147. {
  148. $reporter->paintCaseStart($this->getLabel());
  149. $started = true;
  150. }
  151. $invoker = &$this->_reporter->createInvoker($this->createInvoker());
  152. $invoker->before($method);
  153. $invoker->invoke($method);
  154. $invoker->after($method);
  155. }
  156. }
  157. if ($started)
  158. {
  159. $reporter->paintCaseEnd($this->getLabel());
  160. }
  161. unset($this->_reporter);
  162. return $reporter->getStatus();
  163. }
  164. /**
  165. * Gets a list of test names. Normally that will
  166. * be all internal methods that start with the
  167. * name "test". This method should be overridden
  168. * if you want a different rule.
  169. * @return array List of test names.
  170. * @access public
  171. */
  172. function getTests()
  173. {
  174. $methods = array();
  175. foreach (get_class_methods(get_class($this)) as $method)
  176. {
  177. if ($this->_isTest($method))
  178. {
  179. $methods[] = $method;
  180. }
  181. }
  182. return $methods;
  183. }
  184. /**
  185. * Tests to see if the method is a test that should
  186. * be run. Currently any method that starts with 'test'
  187. * is a candidate unless it is the constructor.
  188. * @param string $method Method name to try.
  189. * @return boolean True if test method.
  190. * @access protected
  191. */
  192. function _isTest($method)
  193. {
  194. if (strtolower(substr($method, 0, 4)) == 'test')
  195. {
  196. return ! SimpleTestCompatibility :: isA($this, strtolower($method));
  197. }
  198. return false;
  199. }
  200. /**
  201. * Announces the start of the test.
  202. * @param string $method Test method just started.
  203. * @access public
  204. */
  205. function before($method)
  206. {
  207. $this->_reporter->paintMethodStart($method);
  208. $this->_observers = array();
  209. }
  210. /**
  211. * Sets up unit test wide variables at the start
  212. * of each test method. To be overridden in
  213. * actual user test cases.
  214. * @access public
  215. */
  216. function setUp()
  217. {
  218. }
  219. /**
  220. * Clears the data set in the setUp() method call.
  221. * To be overridden by the user in actual user test cases.
  222. * @access public
  223. */
  224. function tearDown()
  225. {
  226. }
  227. /**
  228. * Announces the end of the test. Includes private clean up.
  229. * @param string $method Test method just finished.
  230. * @access public
  231. */
  232. function after($method)
  233. {
  234. for($i = 0; $i < count($this->_observers); $i ++)
  235. {
  236. $this->_observers[$i]->atTestEnd($method, $this);
  237. }
  238. $this->_reporter->paintMethodEnd($method);
  239. }
  240. /**
  241. * Sets up an observer for the test end.
  242. * @param object $observer Must have atTestEnd()
  243. * method.
  244. * @access public
  245. */
  246. function tell(&$observer)
  247. {
  248. $this->_observers[] = &$observer;
  249. }
  250. /**
  251. * @deprecated
  252. */
  253. function pass($message = "Pass")
  254. {
  255. if (! isset($this->_reporter))
  256. {
  257. trigger_error('Can only make assertions within test methods');
  258. }
  259. $this->_reporter->paintPass($message . $this->getAssertionLine());
  260. return true;
  261. }
  262. /**
  263. * Sends a fail event with a message.
  264. * @param string $message Message to send.
  265. * @access public
  266. */
  267. function fail($message = "Fail")
  268. {
  269. if (! isset($this->_reporter))
  270. {
  271. trigger_error('Can only make assertions within test methods');
  272. }
  273. $this->_reporter->paintFail($message . $this->getAssertionLine());
  274. return false;
  275. }
  276. /**
  277. * Formats a PHP error and dispatches it to the
  278. * reporter.
  279. * @param integer $severity PHP error code.
  280. * @param string $message Text of error.
  281. * @param string $file File error occoured in.
  282. * @param integer $line Line number of error.
  283. * @access public
  284. */
  285. function error($severity, $message, $file, $line)
  286. {
  287. if (! isset($this->_reporter))
  288. {
  289. trigger_error('Can only make assertions within test methods');
  290. }
  291. $this->_reporter->paintError("Unexpected PHP error [$message] severity [$severity] in [$file line $line]");
  292. }
  293. /**
  294. * Formats an exception and dispatches it to the
  295. * reporter.
  296. * @param Exception $exception Object thrown.
  297. * @access public
  298. */
  299. function exception($exception)
  300. {
  301. $this->_reporter->paintException($exception);
  302. }
  303. /**
  304. * @deprecated
  305. */
  306. function signal($type, &$payload)
  307. {
  308. if (! isset($this->_reporter))
  309. {
  310. trigger_error('Can only make assertions within test methods');
  311. }
  312. $this->_reporter->paintSignal($type, $payload);
  313. }
  314. /**
  315. * Runs an expectation directly, for extending the
  316. * tests with new expectation classes.
  317. * @param SimpleExpectation $expectation Expectation subclass.
  318. * @param mixed $compare Value to compare.
  319. * @param string $message Message to display.
  320. * @return boolean True on pass
  321. * @access public
  322. */
  323. function assert(&$expectation, $compare, $message = '%s')
  324. {
  325. if ($expectation->test($compare))
  326. {
  327. return $this->pass(sprintf($message, $expectation->overlayMessage($compare, $this->_reporter->getDumper())));
  328. }
  329. else
  330. {
  331. return $this->fail(sprintf($message, $expectation->overlayMessage($compare, $this->_reporter->getDumper())));
  332. }
  333. }
  334. /**
  335. * @deprecated
  336. */
  337. function assertExpectation(&$expectation, $compare, $message = '%s')
  338. {
  339. return $this->assert($expectation, $compare, $message);
  340. }
  341. /**
  342. * Uses a stack trace to find the line of an assertion.
  343. * @return string Line number of first assert*
  344. * method embedded in format string.
  345. * @access public
  346. */
  347. function getAssertionLine()
  348. {
  349. $trace = new SimpleStackTrace(array('assert', 'expect', 'pass', 'fail', 'skip'));
  350. return $trace->traceMethod();
  351. }
  352. /**
  353. * Sends a formatted dump of a variable to the
  354. * test suite for those emergency debugging
  355. * situations.
  356. * @param mixed $variable Variable to display.
  357. * @param string $message Message to display.
  358. * @return mixed The original variable.
  359. * @access public
  360. */
  361. function dump($variable, $message = false)
  362. {
  363. $dumper = $this->_reporter->getDumper();
  364. $formatted = $dumper->dump($variable);
  365. if ($message)
  366. {
  367. $formatted = $message . "\n" . $formatted;
  368. }
  369. $this->_reporter->paintFormattedMessage($formatted);
  370. return $variable;
  371. }
  372. /**
  373. * @deprecated
  374. */
  375. function sendMessage($message)
  376. {
  377. $this->_reporter->PaintMessage($message);
  378. }
  379. /**
  380. * Accessor for the number of subtests including myelf.
  381. * @return integer Number of test cases.
  382. * @access public
  383. * @static
  384. */
  385. function getSize()
  386. {
  387. return 1;
  388. }
  389. }
  390. /**
  391. * Helps to extract test cases automatically from a file.
  392. */
  393. class SimpleFileLoader
  394. {
  395. /**
  396. * Builds a test suite from a library of test cases.
  397. * The new suite is composed into this one.
  398. * @param string $test_file File name of library with
  399. * test case classes.
  400. * @return TestSuite The new test suite.
  401. * @access public
  402. */
  403. function &load($test_file)
  404. {
  405. $existing_classes = get_declared_classes();
  406. $existing_globals = get_defined_vars();
  407. include_once ($test_file);
  408. $new_globals = get_defined_vars();
  409. $this->_makeFileVariablesGlobal($existing_globals, $new_globals);
  410. $new_classes = array_diff(get_declared_classes(), $existing_classes);
  411. if (empty($new_classes))
  412. {
  413. $new_classes = $this->_scrapeClassesFromFile($test_file);
  414. }
  415. $classes = $this->selectRunnableTests($new_classes);
  416. $suite = &$this->createSuiteFromClasses($test_file, $classes);
  417. return $suite;
  418. }
  419. /**
  420. * Imports new variables into the global namespace.
  421. * @param hash $existing Variables before the file was loaded.
  422. * @param hash $new Variables after the file was loaded.
  423. * @access private
  424. */
  425. function _makeFileVariablesGlobal($existing, $new)
  426. {
  427. $globals = array_diff(array_keys($new), array_keys($existing));
  428. foreach ($globals as $global)
  429. {
  430. $_GLOBALS[$global] = $new[$global];
  431. }
  432. }
  433. /**
  434. * Lookup classnames from file contents, in case the
  435. * file may have been included before.
  436. * Note: This is probably too clever by half. Figuring this
  437. * out after a failed test case is going to be tricky for us,
  438. * never mind the user. A test case should not be included
  439. * twice anyway.
  440. * @param string $test_file File name with classes.
  441. * @access private
  442. */
  443. function _scrapeClassesFromFile($test_file)
  444. {
  445. preg_match_all('~^\s*class\s+(\w+)(\s+(extends|implements)\s+\w+)*\s*\{~mi', file_get_contents($test_file), $matches);
  446. return $matches[1];
  447. }
  448. /**
  449. * Calculates the incoming test cases. Skips abstract
  450. * and ignored classes.
  451. * @param array $candidates Candidate classes.
  452. * @return array New classes which are test
  453. * cases that shouldn't be ignored.
  454. * @access public
  455. */
  456. function selectRunnableTests($candidates)
  457. {
  458. $classes = array();
  459. foreach ($candidates as $class)
  460. {
  461. if (TestSuite :: getBaseTestCase($class))
  462. {
  463. $reflection = new SimpleReflection($class);
  464. if ($reflection->isAbstract())
  465. {
  466. SimpleTest :: ignore($class);
  467. }
  468. else
  469. {
  470. $classes[] = $class;
  471. }
  472. }
  473. }
  474. return $classes;
  475. }
  476. /**
  477. * Builds a test suite from a class list.
  478. * @param string $title Title of new group.
  479. * @param array $classes Test classes.
  480. * @return TestSuite Group loaded with the new
  481. * test cases.
  482. * @access public
  483. */
  484. function &createSuiteFromClasses($title, $classes)
  485. {
  486. if (count($classes) == 0)
  487. {
  488. $suite = &new BadTestSuite($title, "No runnable test cases in [$title]");
  489. return $suite;
  490. }
  491. SimpleTest :: ignoreParentsIfIgnored($classes);
  492. $suite = &new TestSuite($title);
  493. foreach ($classes as $class)
  494. {
  495. if (! SimpleTest :: isIgnored($class))
  496. {
  497. $suite->addTestClass($class);
  498. }
  499. }
  500. return $suite;
  501. }
  502. }
  503. /**
  504. * This is a composite test class for combining
  505. * test cases and other RunnableTest classes into
  506. * a group test.
  507. * @package SimpleTest
  508. * @subpackage UnitTester
  509. */
  510. class TestSuite
  511. {
  512. var $_label;
  513. var $_test_cases;
  514. /**
  515. * Sets the name of the test suite.
  516. * @param string $label Name sent at the start and end
  517. * of the test.
  518. * @access public
  519. */
  520. function __construct($label = false)
  521. {
  522. $this->_label = $label;
  523. $this->_test_cases = array();
  524. }
  525. /**
  526. * Accessor for the test name for subclasses. If the suite
  527. * wraps a single test case the label defaults to the name of that test.
  528. * @return string Name of the test.
  529. * @access public
  530. */
  531. function getLabel()
  532. {
  533. if (! $this->_label)
  534. {
  535. return ($this->getSize() == 1) ? get_class($this->_test_cases[0]) : get_class($this);
  536. }
  537. else
  538. {
  539. return $this->_label;
  540. }
  541. }
  542. /**
  543. * @deprecated
  544. */
  545. function addTestCase(&$test_case)
  546. {
  547. $this->_test_cases[] = &$test_case;
  548. }
  549. /**
  550. * @deprecated
  551. */
  552. function addTestClass($class)
  553. {
  554. if (TestSuite :: getBaseTestCase($class) == 'testsuite')
  555. {
  556. $this->_test_cases[] = &new $class();
  557. }
  558. else
  559. {
  560. $this->_test_cases[] = $class;
  561. }
  562. }
  563. /**
  564. * Adds a test into the suite by instance or class. The class will
  565. * be instantiated if it's a test suite.
  566. * @param SimpleTestCase $test_case Suite or individual test
  567. * case implementing the
  568. * runnable test interface.
  569. * @access public
  570. */
  571. function add(&$test_case)
  572. {
  573. if (! is_string($test_case))
  574. {
  575. $this->_test_cases[] = &$test_case;
  576. }
  577. elseif (TestSuite :: getBaseTestCase($class) == 'testsuite')
  578. {
  579. $this->_test_cases[] = &new $class();
  580. }
  581. else
  582. {
  583. $this->_test_cases[] = $class;
  584. }
  585. }
  586. /**
  587. * @deprecated
  588. */
  589. function addTestFile($test_file)
  590. {
  591. $this->addFile($test_file);
  592. }
  593. /**
  594. * Builds a test suite from a library of test cases.
  595. * The new suite is composed into this one.
  596. * @param string $test_file File name of library with
  597. * test case classes.
  598. * @access public
  599. */
  600. function addFile($test_file)
  601. {
  602. $extractor = new SimpleFileLoader();
  603. $this->add($extractor->load($test_file));
  604. }
  605. /**
  606. * Delegates to a visiting collector to add test
  607. * files.
  608. * @param string $path Path to scan from.
  609. * @param SimpleCollector $collector Directory scanner.
  610. * @access public
  611. */
  612. function collect($path, &$collector)
  613. {
  614. $collector->collect($this, $path);
  615. }
  616. /**
  617. * Invokes run() on all of the held test cases, instantiating
  618. * them if necessary.
  619. * @param SimpleReporter $reporter Current test reporter.
  620. * @access public
  621. */
  622. function run(&$reporter)
  623. {
  624. $reporter->paintGroupStart($this->getLabel(), $this->getSize());
  625. for($i = 0, $count = count($this->_test_cases); $i < $count; $i ++)
  626. {
  627. if (is_string($this->_test_cases[$i]))
  628. {
  629. $class = $this->_test_cases[$i];
  630. $test = &new $class();
  631. $test->run($reporter);
  632. unset($test);
  633. }
  634. else
  635. {
  636. $this->_test_cases[$i]->run($reporter);
  637. }
  638. }
  639. $reporter->paintGroupEnd($this->getLabel());
  640. return $reporter->getStatus();
  641. }
  642. /**
  643. * Number of contained test cases.
  644. * @return integer Total count of cases in the group.
  645. * @access public
  646. */
  647. function getSize()
  648. {
  649. $count = 0;
  650. foreach ($this->_test_cases as $case)
  651. {
  652. if (is_string($case))
  653. {
  654. if (! SimpleTest :: isIgnored($case))
  655. {
  656. $count ++;
  657. }
  658. }
  659. else
  660. {
  661. $count += $case->getSize();
  662. }
  663. }
  664. return $count;
  665. }
  666. /**
  667. * Test to see if a class is derived from the
  668. * SimpleTestCase class.
  669. * @param string $class Class name.
  670. * @access public
  671. * @static
  672. */
  673. function getBaseTestCase($class)
  674. {
  675. while ($class = get_parent_class($class))
  676. {
  677. $class = strtolower($class);
  678. if ($class == 'simpletestcase' || $class == 'testsuite')
  679. {
  680. return $class;
  681. }
  682. }
  683. return false;
  684. }
  685. }
  686. /**
  687. * @package SimpleTest
  688. * @subpackage UnitTester
  689. * @deprecated
  690. */
  691. class GroupTest extends TestSuite
  692. {
  693. }
  694. /**
  695. * This is a failing group test for when a test suite hasn't
  696. * loaded properly.
  697. * @package SimpleTest
  698. * @subpackage UnitTester
  699. */
  700. class BadTestSuite
  701. {
  702. var $_label;
  703. var $_error;
  704. /**
  705. * Sets the name of the test suite and error message.
  706. * @param string $label Name sent at the start and end
  707. * of the test.
  708. * @access public
  709. */
  710. function __construct($label, $error)
  711. {
  712. $this->_label = $label;
  713. $this->_error = $error;
  714. }
  715. /**
  716. * Accessor for the test name for subclasses.
  717. * @return string Name of the test.
  718. * @access public
  719. */
  720. function getLabel()
  721. {
  722. return $this->_label;
  723. }
  724. /**
  725. * Sends a single error to the reporter.
  726. * @param SimpleReporter $reporter Current test reporter.
  727. * @access public
  728. */
  729. function run(&$reporter)
  730. {
  731. $reporter->paintGroupStart($this->getLabel(), $this->getSize());
  732. $reporter->paintFail('Bad TestSuite [' . $this->getLabel() . '] with error [' . $this->_error . ']');
  733. $reporter->paintGroupEnd($this->getLabel());
  734. return $reporter->getStatus();
  735. }
  736. /**
  737. * Number of contained test cases. Always zero.
  738. * @return integer Total count of cases in the group.
  739. * @access public
  740. */
  741. function getSize()
  742. {
  743. return 0;
  744. }
  745. }
  746. /**
  747. * @package SimpleTest
  748. * @subpackage UnitTester
  749. * @deprecated
  750. */
  751. class BadGroupTest extends BadTestSuite
  752. {
  753. }
  754. ?>