PageRenderTime 55ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/webapp/_lib/extlib/simpletest/test_case.php

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