PageRenderTime 54ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/application/libraries/simpletest/test_case.php

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