PageRenderTime 58ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/mock_objects.php

https://bitbucket.org/photocrati/simpletest-for-wordpress
PHP | 1641 lines | 797 code | 111 blank | 733 comment | 65 complexity | 4243ff3224d3f3f38ccb8c053ed69d3d MD5 | raw file
Possible License(s): LGPL-2.1

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * base include file for SimpleTest
  4. * @package SimpleTest
  5. * @subpackage MockObjects
  6. * @version $Id: mock_objects.php 1973 2009-12-22 01:16:59Z lastcraft $
  7. */
  8. /**#@+
  9. * include SimpleTest files
  10. */
  11. require_once(dirname(__FILE__) . '/expectation.php');
  12. require_once(dirname(__FILE__) . '/simpletest.php');
  13. require_once(dirname(__FILE__) . '/dumper.php');
  14. require_once(dirname(__FILE__) . '/reflection_php5.php');
  15. /**#@-*/
  16. /**
  17. * Default character simpletest will substitute for any value
  18. */
  19. if (! defined('MOCK_ANYTHING')) {
  20. define('MOCK_ANYTHING', '*');
  21. }
  22. /**
  23. * Parameter comparison assertion.
  24. * @package SimpleTest
  25. * @subpackage MockObjects
  26. */
  27. class ParametersExpectation extends SimpleExpectation {
  28. private $expected;
  29. /**
  30. * Sets the expected parameter list.
  31. * @param array $parameters Array of parameters including
  32. * those that are wildcarded.
  33. * If the value is not an array
  34. * then it is considered to match any.
  35. * @param string $message Customised message on failure.
  36. */
  37. function __construct($expected = false, $message = '%s') {
  38. parent::__construct($message);
  39. $this->expected = $expected;
  40. }
  41. /**
  42. * Tests the assertion. True if correct.
  43. * @param array $parameters Comparison values.
  44. * @return boolean True if correct.
  45. */
  46. function test($parameters) {
  47. if (! is_array($this->expected)) {
  48. return true;
  49. }
  50. if (count($this->expected) != count($parameters)) {
  51. return false;
  52. }
  53. for ($i = 0; $i < count($this->expected); $i++) {
  54. if (! $this->testParameter($parameters[$i], $this->expected[$i])) {
  55. return false;
  56. }
  57. }
  58. return true;
  59. }
  60. /**
  61. * Tests an individual parameter.
  62. * @param mixed $parameter Value to test.
  63. * @param mixed $expected Comparison value.
  64. * @return boolean True if expectation
  65. * fulfilled.
  66. */
  67. protected function testParameter($parameter, $expected) {
  68. $comparison = $this->coerceToExpectation($expected);
  69. return $comparison->test($parameter);
  70. }
  71. /**
  72. * Returns a human readable test message.
  73. * @param array $comparison Incoming parameter list.
  74. * @return string Description of success
  75. * or failure.
  76. */
  77. function testMessage($parameters) {
  78. if ($this->test($parameters)) {
  79. return "Expectation of " . count($this->expected) .
  80. " arguments of [" . $this->renderArguments($this->expected) .
  81. "] is correct";
  82. } else {
  83. return $this->describeDifference($this->expected, $parameters);
  84. }
  85. }
  86. /**
  87. * Message to display if expectation differs from
  88. * the parameters actually received.
  89. * @param array $expected Expected parameters as list.
  90. * @param array $parameters Actual parameters received.
  91. * @return string Description of difference.
  92. */
  93. protected function describeDifference($expected, $parameters) {
  94. if (count($expected) != count($parameters)) {
  95. return "Expected " . count($expected) .
  96. " arguments of [" . $this->renderArguments($expected) .
  97. "] but got " . count($parameters) .
  98. " arguments of [" . $this->renderArguments($parameters) . "]";
  99. }
  100. $messages = array();
  101. for ($i = 0; $i < count($expected); $i++) {
  102. $comparison = $this->coerceToExpectation($expected[$i]);
  103. if (! $comparison->test($parameters[$i])) {
  104. $messages[] = "parameter " . ($i + 1) . " with [" .
  105. $comparison->overlayMessage($parameters[$i], $this->getDumper()) . "]";
  106. }
  107. }
  108. return "Parameter expectation differs at " . implode(" and ", $messages);
  109. }
  110. /**
  111. * Creates an identical expectation if the
  112. * object/value is not already some type
  113. * of expectation.
  114. * @param mixed $expected Expected value.
  115. * @return SimpleExpectation Expectation object.
  116. */
  117. protected function coerceToExpectation($expected) {
  118. if (SimpleExpectation::isExpectation($expected)) {
  119. return $expected;
  120. }
  121. return new IdenticalExpectation($expected);
  122. }
  123. /**
  124. * Renders the argument list as a string for
  125. * messages.
  126. * @param array $args Incoming arguments.
  127. * @return string Simple description of type and value.
  128. */
  129. protected function renderArguments($args) {
  130. $descriptions = array();
  131. if (is_array($args)) {
  132. foreach ($args as $arg) {
  133. $dumper = new SimpleDumper();
  134. $descriptions[] = $dumper->describeValue($arg);
  135. }
  136. }
  137. return implode(', ', $descriptions);
  138. }
  139. }
  140. /**
  141. * Confirms that the number of calls on a method is as expected.
  142. * @package SimpleTest
  143. * @subpackage MockObjects
  144. */
  145. class CallCountExpectation extends SimpleExpectation {
  146. private $method;
  147. private $count;
  148. /**
  149. * Stashes the method and expected count for later
  150. * reporting.
  151. * @param string $method Name of method to confirm against.
  152. * @param integer $count Expected number of calls.
  153. * @param string $message Custom error message.
  154. */
  155. function __construct($method, $count, $message = '%s') {
  156. $this->method = $method;
  157. $this->count = $count;
  158. parent::__construct($message);
  159. }
  160. /**
  161. * Tests the assertion. True if correct.
  162. * @param integer $compare Measured call count.
  163. * @return boolean True if expected.
  164. */
  165. function test($compare) {
  166. return ($this->count == $compare);
  167. }
  168. /**
  169. * Reports the comparison.
  170. * @param integer $compare Measured call count.
  171. * @return string Message to show.
  172. */
  173. function testMessage($compare) {
  174. return 'Expected call count for [' . $this->method .
  175. '] was [' . $this->count .
  176. '] got [' . $compare . ']';
  177. }
  178. }
  179. /**
  180. * Confirms that the number of calls on a method is as expected.
  181. * @package SimpleTest
  182. * @subpackage MockObjects
  183. */
  184. class MinimumCallCountExpectation extends SimpleExpectation {
  185. private $method;
  186. private $count;
  187. /**
  188. * Stashes the method and expected count for later
  189. * reporting.
  190. * @param string $method Name of method to confirm against.
  191. * @param integer $count Minimum number of calls.
  192. * @param string $message Custom error message.
  193. */
  194. function __construct($method, $count, $message = '%s') {
  195. $this->method = $method;
  196. $this->count = $count;
  197. parent::__construct($message);
  198. }
  199. /**
  200. * Tests the assertion. True if correct.
  201. * @param integer $compare Measured call count.
  202. * @return boolean True if enough.
  203. */
  204. function test($compare) {
  205. return ($this->count <= $compare);
  206. }
  207. /**
  208. * Reports the comparison.
  209. * @param integer $compare Measured call count.
  210. * @return string Message to show.
  211. */
  212. function testMessage($compare) {
  213. return 'Minimum call count for [' . $this->method .
  214. '] was [' . $this->count .
  215. '] got [' . $compare . ']';
  216. }
  217. }
  218. /**
  219. * Confirms that the number of calls on a method is as expected.
  220. * @package SimpleTest
  221. * @subpackage MockObjects
  222. */
  223. class MaximumCallCountExpectation extends SimpleExpectation {
  224. private $method;
  225. private $count;
  226. /**
  227. * Stashes the method and expected count for later
  228. * reporting.
  229. * @param string $method Name of method to confirm against.
  230. * @param integer $count Minimum number of calls.
  231. * @param string $message Custom error message.
  232. */
  233. function __construct($method, $count, $message = '%s') {
  234. $this->method = $method;
  235. $this->count = $count;
  236. parent::__construct($message);
  237. }
  238. /**
  239. * Tests the assertion. True if correct.
  240. * @param integer $compare Measured call count.
  241. * @return boolean True if not over.
  242. */
  243. function test($compare) {
  244. return ($this->count >= $compare);
  245. }
  246. /**
  247. * Reports the comparison.
  248. * @param integer $compare Measured call count.
  249. * @return string Message to show.
  250. */
  251. function testMessage($compare) {
  252. return 'Maximum call count for [' . $this->method .
  253. '] was [' . $this->count .
  254. '] got [' . $compare . ']';
  255. }
  256. }
  257. /**
  258. * Retrieves method actions by searching the
  259. * parameter lists until an expected match is found.
  260. * @package SimpleTest
  261. * @subpackage MockObjects
  262. */
  263. class SimpleSignatureMap {
  264. private $map;
  265. /**
  266. * Creates an empty call map.
  267. */
  268. function __construct() {
  269. $this->map = array();
  270. }
  271. /**
  272. * Stashes a reference against a method call.
  273. * @param array $parameters Array of arguments (including wildcards).
  274. * @param mixed $action Reference placed in the map.
  275. */
  276. function add($parameters, $action) {
  277. $place = count($this->map);
  278. $this->map[$place] = array();
  279. $this->map[$place]['params'] = new ParametersExpectation($parameters);
  280. $this->map[$place]['content'] = $action;
  281. }
  282. /**
  283. * Searches the call list for a matching parameter
  284. * set. Returned by reference.
  285. * @param array $parameters Parameters to search by
  286. * without wildcards.
  287. * @return object Object held in the first matching
  288. * slot, otherwise null.
  289. */
  290. function &findFirstAction($parameters) {
  291. $slot = $this->findFirstSlot($parameters);
  292. if (isset($slot) && isset($slot['content'])) {
  293. return $slot['content'];
  294. }
  295. $null = null;
  296. return $null;
  297. }
  298. /**
  299. * Searches the call list for a matching parameter
  300. * set. True if successful.
  301. * @param array $parameters Parameters to search by
  302. * without wildcards.
  303. * @return boolean True if a match is present.
  304. */
  305. function isMatch($parameters) {
  306. return ($this->findFirstSlot($parameters) != null);
  307. }
  308. /**
  309. * Compares the incoming parameters with the
  310. * internal expectation. Uses the incoming $test
  311. * to dispatch the test message.
  312. * @param SimpleTestCase $test Test to dispatch to.
  313. * @param array $parameters The actual calling arguments.
  314. * @param string $message The message to overlay.
  315. */
  316. function test($test, $parameters, $message) {
  317. }
  318. /**
  319. * Searches the map for a matching item.
  320. * @param array $parameters Parameters to search by
  321. * without wildcards.
  322. * @return array Reference to slot or null.
  323. */
  324. function &findFirstSlot($parameters) {
  325. $count = count($this->map);
  326. for ($i = 0; $i < $count; $i++) {
  327. if ($this->map[$i]["params"]->test($parameters)) {
  328. return $this->map[$i];
  329. }
  330. }
  331. $null = null;
  332. return $null;
  333. }
  334. }
  335. /**
  336. * Allows setting of actions against call signatures either
  337. * at a specific time, or always. Specific time settings
  338. * trump lasting ones, otherwise the most recently added
  339. * will mask an earlier match.
  340. * @package SimpleTest
  341. * @subpackage MockObjects
  342. */
  343. class SimpleCallSchedule {
  344. private $wildcard = MOCK_ANYTHING;
  345. private $always;
  346. private $at;
  347. /**
  348. * Sets up an empty response schedule.
  349. * Creates an empty call map.
  350. */
  351. function __construct() {
  352. $this->always = array();
  353. $this->at = array();
  354. }
  355. /**
  356. * Stores an action against a signature that
  357. * will always fire unless masked by a time
  358. * specific one.
  359. * @param string $method Method name.
  360. * @param array $args Calling parameters.
  361. * @param SimpleAction $action Actually simpleByValue, etc.
  362. */
  363. function register($method, $args, $action) {
  364. $args = $this->replaceWildcards($args);
  365. $method = strtolower($method);
  366. if (! isset($this->always[$method])) {
  367. $this->always[$method] = new SimpleSignatureMap();
  368. }
  369. $this->always[$method]->add($args, $action);
  370. }
  371. /**
  372. * Stores an action against a signature that
  373. * will fire at a specific time in the future.
  374. * @param integer $step delay of calls to this method,
  375. * 0 is next.
  376. * @param string $method Method name.
  377. * @param array $args Calling parameters.
  378. * @param SimpleAction $action Actually SimpleByValue, etc.
  379. */
  380. function registerAt($step, $method, $args, $action) {
  381. $args = $this->replaceWildcards($args);
  382. $method = strtolower($method);
  383. if (! isset($this->at[$method])) {
  384. $this->at[$method] = array();
  385. }
  386. if (! isset($this->at[$method][$step])) {
  387. $this->at[$method][$step] = new SimpleSignatureMap();
  388. }
  389. $this->at[$method][$step]->add($args, $action);
  390. }
  391. /**
  392. * Sets up an expectation on the argument list.
  393. * @param string $method Method to test.
  394. * @param array $args Bare arguments or list of
  395. * expectation objects.
  396. * @param string $message Failure message.
  397. */
  398. function expectArguments($method, $args, $message) {
  399. $args = $this->replaceWildcards($args);
  400. $message .= Mock::getExpectationLine();
  401. $this->expected_args[strtolower($method)] =
  402. new ParametersExpectation($args, $message);
  403. }
  404. /**
  405. * Actually carry out the action stored previously,
  406. * if the parameters match.
  407. * @param integer $step Time of call.
  408. * @param string $method Method name.
  409. * @param array $args The parameters making up the
  410. * rest of the call.
  411. * @return mixed The result of the action.
  412. */
  413. function &respond($step, $method, $args) {
  414. $method = strtolower($method);
  415. if (isset($this->at[$method][$step])) {
  416. if ($this->at[$method][$step]->isMatch($args)) {
  417. $action = $this->at[$method][$step]->findFirstAction($args);
  418. if (isset($action)) {
  419. return $action->act();
  420. }
  421. }
  422. }
  423. if (isset($this->always[$method])) {
  424. $action = $this->always[$method]->findFirstAction($args);
  425. if (isset($action)) {
  426. return $action->act();
  427. }
  428. }
  429. $null = null;
  430. return $null;
  431. }
  432. /**
  433. * Replaces wildcard matches with wildcard
  434. * expectations in the argument list.
  435. * @param array $args Raw argument list.
  436. * @return array Argument list with
  437. * expectations.
  438. */
  439. protected function replaceWildcards($args) {
  440. if ($args === false) {
  441. return false;
  442. }
  443. for ($i = 0; $i < count($args); $i++) {
  444. if ($args[$i] === $this->wildcard) {
  445. $args[$i] = new AnythingExpectation();
  446. }
  447. }
  448. return $args;
  449. }
  450. }
  451. /**
  452. * A type of SimpleMethodAction.
  453. * Stashes a value for returning later. Follows usual
  454. * PHP5 semantics of objects being returned by reference.
  455. * @package SimpleTest
  456. * @subpackage MockObjects
  457. */
  458. class SimpleReturn {
  459. private $value;
  460. /**
  461. * Stashes it for later.
  462. * @param mixed $value You need to clone objects
  463. * if you want copy semantics
  464. * for these.
  465. */
  466. function __construct($value) {
  467. $this->value = $value;
  468. }
  469. /**
  470. * Returns the value stored earlier.
  471. * @return mixed Whatever was stashed.
  472. */
  473. function act() {
  474. return $this->value;
  475. }
  476. }
  477. /**
  478. * A type of SimpleMethodAction.
  479. * Stashes a reference for returning later.
  480. * @package SimpleTest
  481. * @subpackage MockObjects
  482. */
  483. class SimpleByReference {
  484. private $reference;
  485. /**
  486. * Stashes it for later.
  487. * @param mixed $reference Actual PHP4 style reference.
  488. */
  489. function __construct(&$reference) {
  490. $this->reference = &$reference;
  491. }
  492. /**
  493. * Returns the reference stored earlier.
  494. * @return mixed Whatever was stashed.
  495. */
  496. function &act() {
  497. return $this->reference;
  498. }
  499. }
  500. /**
  501. * A type of SimpleMethodAction.
  502. * Stashes a value for returning later.
  503. * @package SimpleTest
  504. * @subpackage MockObjects
  505. */
  506. class SimpleByValue {
  507. private $value;
  508. /**
  509. * Stashes it for later.
  510. * @param mixed $value You need to clone objects
  511. * if you want copy semantics
  512. * for these.
  513. */
  514. function __construct($value) {
  515. $this->value = $value;
  516. }
  517. /**
  518. * Returns the value stored earlier.
  519. * @return mixed Whatever was stashed.
  520. */
  521. function &act() {
  522. $dummy = $this->value;
  523. return $dummy;
  524. }
  525. }
  526. /**
  527. * A type of SimpleMethodAction.
  528. * Stashes an exception for throwing later.
  529. * @package SimpleTest
  530. * @subpackage MockObjects
  531. */
  532. class SimpleThrower {
  533. private $exception;
  534. /**
  535. * Stashes it for later.
  536. * @param Exception $exception The exception object to throw.
  537. */
  538. function __construct($exception) {
  539. $this->exception = $exception;
  540. }
  541. /**
  542. * Throws the exceptins stashed earlier.
  543. */
  544. function act() {
  545. throw $this->exception;
  546. }
  547. }
  548. /**
  549. * A type of SimpleMethodAction.
  550. * Stashes an error for emitting later.
  551. * @package SimpleTest
  552. * @subpackage MockObjects
  553. */
  554. class SimpleErrorThrower {
  555. private $error;
  556. private $severity;
  557. /**
  558. * Stashes an error to throw later.
  559. * @param string $error Error message.
  560. * @param integer $severity PHP error constant, e.g E_USER_ERROR.
  561. */
  562. function __construct($error, $severity) {
  563. $this->error = $error;
  564. $this->severity = $severity;
  565. }
  566. /**
  567. * Triggers the stashed error.
  568. */
  569. function &act() {
  570. trigger_error($this->error, $this->severity);
  571. $null = null;
  572. return $null;
  573. }
  574. }
  575. /**
  576. * A base class or delegate that extends an
  577. * empty collection of methods that can have their
  578. * return values set and expectations made of the
  579. * calls upon them. The mock will assert the
  580. * expectations against it's attached test case in
  581. * addition to the server stub behaviour or returning
  582. * preprogrammed responses.
  583. * @package SimpleTest
  584. * @subpackage MockObjects
  585. */
  586. class SimpleMock {
  587. private $actions;
  588. private $expectations;
  589. private $wildcard = MOCK_ANYTHING;
  590. private $is_strict = true;
  591. private $call_counts;
  592. private $expected_counts;
  593. private $max_counts;
  594. private $expected_args;
  595. private $expected_args_at;
  596. /**
  597. * Creates an empty action list and expectation list.
  598. * All call counts are set to zero.
  599. */
  600. function SimpleMock() {
  601. $this->actions = new SimpleCallSchedule();
  602. $this->expectations = new SimpleCallSchedule();
  603. $this->call_counts = array();
  604. $this->expected_counts = array();
  605. $this->max_counts = array();
  606. $this->expected_args = array();
  607. $this->expected_args_at = array();
  608. $this->getCurrentTestCase()->tell($this);
  609. }
  610. /**
  611. * Disables a name check when setting expectations.
  612. * This hack is needed for the partial mocks.
  613. */
  614. function disableExpectationNameChecks() {
  615. $this->is_strict = false;
  616. }
  617. /**
  618. * Finds currently running test.
  619. * @return SimpeTestCase Current test case.
  620. */
  621. protected function getCurrentTestCase() {
  622. return SimpleTest::getContext()->getTest();
  623. }
  624. /**
  625. * Die if bad arguments array is passed.
  626. * @param mixed $args The arguments value to be checked.
  627. * @param string $task Description of task attempt.
  628. * @return boolean Valid arguments
  629. */
  630. protected function checkArgumentsIsArray($args, $task) {
  631. if (! is_array($args)) {
  632. trigger_error(
  633. "Cannot $task as \$args parameter is not an array",
  634. E_USER_ERROR);
  635. }
  636. }
  637. /**
  638. * Triggers a PHP error if the method is not part
  639. * of this object.
  640. * @param string $method Name of method.
  641. * @param string $task Description of task attempt.
  642. */
  643. protected function dieOnNoMethod($method, $task) {
  644. if ($this->is_strict && ! method_exists($this, $method)) {
  645. trigger_error(
  646. "Cannot $task as no ${method}() in class " . get_class($this),
  647. E_USER_ERROR);
  648. }
  649. }
  650. /**
  651. * Replaces wildcard matches with wildcard
  652. * expectations in the argument list.
  653. * @param array $args Raw argument list.
  654. * @return array Argument list with
  655. * expectations.
  656. */
  657. function replaceWildcards($args) {
  658. if ($args === false) {
  659. return false;
  660. }
  661. for ($i = 0; $i < count($args); $i++) {
  662. if ($args[$i] === $this->wildcard) {
  663. $args[$i] = new AnythingExpectation();
  664. }
  665. }
  666. return $args;
  667. }
  668. /**
  669. * Adds one to the call count of a method.
  670. * @param string $method Method called.
  671. * @param array $args Arguments as an array.
  672. */
  673. protected function addCall($method, $args) {
  674. if (! isset($this->call_counts[$method])) {
  675. $this->call_counts[$method] = 0;
  676. }
  677. $this->call_counts[$method]++;
  678. }
  679. /**
  680. * Fetches the call count of a method so far.
  681. * @param string $method Method name called.
  682. * @return integer Number of calls so far.
  683. */
  684. function getCallCount($method) {
  685. $this->dieOnNoMethod($method, "get call count");
  686. $method = strtolower($method);
  687. if (! isset($this->call_counts[$method])) {
  688. return 0;
  689. }
  690. return $this->call_counts[$method];
  691. }
  692. /**
  693. * Sets a return for a parameter list that will
  694. * be passed on by all calls to this method that match.
  695. * @param string $method Method name.
  696. * @param mixed $value Result of call by value/handle.
  697. * @param array $args List of parameters to match
  698. * including wildcards.
  699. */
  700. function returns($method, $value, $args = false) {
  701. $this->dieOnNoMethod($method, "set return");
  702. $this->actions->register($method, $args, new SimpleReturn($value));
  703. }
  704. /**
  705. * Sets a return for a parameter list that will
  706. * be passed only when the required call count
  707. * is reached.
  708. * @param integer $timing Number of calls in the future
  709. * to which the result applies. If
  710. * not set then all calls will return
  711. * the value.
  712. * @param string $method Method name.
  713. * @param mixed $value Result of call passed.
  714. * @param array $args List of parameters to match
  715. * including wildcards.
  716. */
  717. function returnsAt($timing, $method, $value, $args = false) {
  718. $this->dieOnNoMethod($method, "set return value sequence");
  719. $this->actions->registerAt($timing, $method, $args, new SimpleReturn($value));
  720. }
  721. /**
  722. * Sets a return for a parameter list that will
  723. * be passed by value for all calls to this method.
  724. * @param string $method Method name.
  725. * @param mixed $value Result of call passed by value.
  726. * @param array $args List of parameters to match
  727. * including wildcards.
  728. */
  729. function returnsByValue($method, $value, $args = false) {
  730. $this->dieOnNoMethod($method, "set return value");
  731. $this->actions->register($method, $args, new SimpleByValue($value));
  732. }
  733. /** @deprecated */
  734. function setReturnValue($method, $value, $args = false) {
  735. $this->returnsByValue($method, $value, $args);
  736. }
  737. /**
  738. * Sets a return for a parameter list that will
  739. * be passed by value only when the required call count
  740. * is reached.
  741. * @param integer $timing Number of calls in the future
  742. * to which the result applies. If
  743. * not set then all calls will return
  744. * the value.
  745. * @param string $method Method name.
  746. * @param mixed $value Result of call passed by value.
  747. * @param array $args List of parameters to match
  748. * including wildcards.
  749. */
  750. function returnsByValueAt($timing, $method, $value, $args = false) {
  751. $this->dieOnNoMethod($method, "set return value sequence");
  752. $this->actions->registerAt($timing, $method, $args, new SimpleByValue($value));
  753. }
  754. /** @deprecated */
  755. function setReturnValueAt($timing, $method, $value, $args = false) {
  756. $this->returnsByValueAt($timing, $method, $value, $args);
  757. }
  758. /**
  759. * Sets a return for a parameter list that will
  760. * be passed by reference for all calls.
  761. * @param string $method Method name.
  762. * @param mixed $reference Result of the call will be this object.
  763. * @param array $args List of parameters to match
  764. * including wildcards.
  765. */
  766. function returnsByReference($method, &$reference, $args = false) {
  767. $this->dieOnNoMethod($method, "set return reference");
  768. $this->actions->register($method, $args, new SimpleByReference($reference));
  769. }
  770. /** @deprecated */
  771. function setReturnReference($method, &$reference, $args = false) {
  772. $this->returnsByReference($method, $reference, $args);
  773. }
  774. /**
  775. * Sets a return for a parameter list that will
  776. * be passed by value only when the required call count
  777. * is reached.
  778. * @param integer $timing Number of calls in the future
  779. * to which the result applies. If
  780. * not set then all calls will return
  781. * the value.
  782. * @param string $method Method name.
  783. * @param mixed $reference Result of the call will be this object.
  784. * @param array $args List of parameters to match
  785. * including wildcards.
  786. */
  787. function returnsByReferenceAt($timing, $method, &$reference, $args = false) {
  788. $this->dieOnNoMethod($method, "set return reference sequence");
  789. $this->actions->registerAt($timing, $method, $args, new SimpleByReference($reference));
  790. }
  791. /** @deprecated */
  792. function setReturnReferenceAt($timing, $method, &$reference, $args = false) {
  793. $this->returnsByReferenceAt($timing, $method, $reference, $args);
  794. }
  795. /**
  796. * Sets up an expected call with a set of
  797. * expected parameters in that call. All
  798. * calls will be compared to these expectations
  799. * regardless of when the call is made.
  800. * @param string $method Method call to test.
  801. * @param array $args Expected parameters for the call
  802. * including wildcards.
  803. * @param string $message Overridden message.
  804. */
  805. function expect($method, $args, $message = '%s') {
  806. $this->dieOnNoMethod($method, 'set expected arguments');
  807. $this->checkArgumentsIsArray($args, 'set expected arguments');
  808. $this->expectations->expectArguments($method, $args, $message);
  809. $args = $this->replaceWildcards($args);
  810. $message .= Mock::getExpectationLine();
  811. $this->expected_args[strtolower($method)] =
  812. new ParametersExpectation($args, $message);
  813. }
  814. /**
  815. * Sets up an expected call with a set of
  816. * expected parameters in that call. The
  817. * expected call count will be adjusted if it
  818. * is set too low to reach this call.
  819. * @param integer $timing Number of calls in the future at
  820. * which to test. Next call is 0.
  821. * @param string $method Method call to test.
  822. * @param array $args Expected parameters for the call
  823. * including wildcards.
  824. * @param string $message Overridden message.
  825. */
  826. function expectAt($timing, $method, $args, $message = '%s') {
  827. $this->dieOnNoMethod($method, 'set expected arguments at time');
  828. $this->checkArgumentsIsArray($args, 'set expected arguments at time');
  829. $args = $this->replaceWildcards($args);
  830. if (! isset($this->expected_args_at[$timing])) {
  831. $this->expected_args_at[$timing] = array();
  832. }
  833. $method = strtolower($method);
  834. $message .= Mock::getExpectationLine();
  835. $this->expected_args_at[$timing][$method] =
  836. new ParametersExpectation($args, $message);
  837. }
  838. /**
  839. * Sets an expectation for the number of times
  840. * a method will be called. The tally method
  841. * is used to check this.
  842. * @param string $method Method call to test.
  843. * @param integer $count Number of times it should
  844. * have been called at tally.
  845. * @param string $message Overridden message.
  846. */
  847. function expectCallCount($method, $count, $message = '%s') {
  848. $this->dieOnNoMethod($method, 'set expected call count');
  849. $message .= Mock::getExpectationLine();
  850. $this->expected_counts[strtolower($method)] =
  851. new CallCountExpectation($method, $count, $message);
  852. }
  853. /**
  854. * Sets the number of times a method may be called
  855. * before a test failure is triggered.
  856. * @param string $method Method call to test.
  857. * @param integer $count Most number of times it should
  858. * have been called.
  859. * @param string $message Overridden message.
  860. */
  861. function expectMaximumCallCount($method, $count, $message = '%s') {
  862. $this->dieOnNoMethod($method, 'set maximum call count');
  863. $message .= Mock::getExpectationLine();
  864. $this->max_counts[strtolower($method)] =
  865. new MaximumCallCountExpectation($method, $count, $message);
  866. }
  867. /**
  868. * Sets the number of times to call a method to prevent
  869. * a failure on the tally.
  870. * @param string $method Method call to test.
  871. * @param integer $count Least number of times it should
  872. * have been called.
  873. * @param string $message Overridden message.
  874. */
  875. function expectMinimumCallCount($method, $count, $message = '%s') {
  876. $this->dieOnNoMethod($method, 'set minimum call count');
  877. $message .= Mock::getExpectationLine();
  878. $this->expected_counts[strtolower($method)] =
  879. new MinimumCallCountExpectation($method, $count, $message);
  880. }
  881. /**
  882. * Convenience method for barring a method
  883. * call.
  884. * @param string $method Method call to ban.
  885. * @param string $message Overridden message.
  886. */
  887. function expectNever($method, $message = '%s') {
  888. $this->expectMaximumCallCount($method, 0, $message);
  889. }
  890. /**
  891. * Convenience method for a single method
  892. * call.
  893. * @param string $method Method call to track.
  894. * @param array $args Expected argument list or
  895. * false for any arguments.
  896. * @param string $message Overridden message.
  897. */
  898. function expectOnce($method, $args = false, $message = '%s') {
  899. $this->expectCallCount($method, 1, $message);
  900. if ($args !== false) {
  901. $this->expect($method, $args, $message);
  902. }
  903. }
  904. /**
  905. * Convenience method for requiring a method
  906. * call.
  907. * @param string $method Method call to track.
  908. * @param array $args Expected argument list or
  909. * false for any arguments.
  910. * @param string $message Overridden message.
  911. */
  912. function expectAtLeastOnce($method, $args = false, $message = '%s') {
  913. $this->expectMinimumCallCount($method, 1, $message);
  914. if ($args !== false) {
  915. $this->expect($method, $args, $message);
  916. }
  917. }
  918. /**
  919. * Sets up a trigger to throw an exception upon the
  920. * method call.
  921. * @param string $method Method name to throw on.
  922. * @param object $exception Exception object to throw.
  923. * If not given then a simple
  924. * Exception object is thrown.
  925. * @param array $args Optional argument list filter.
  926. * If given then the exception
  927. * will only be thrown if the
  928. * method call matches the arguments.
  929. */
  930. function throwOn($method, $exception = false, $args = false) {
  931. $this->dieOnNoMethod($method, "throw on");
  932. $this->actions->register($method, $args,
  933. new SimpleThrower($exception ? $exception : new Exception()));
  934. }
  935. /**
  936. * Sets up a trigger to throw an exception upon the
  937. * method call.
  938. * @param integer $timing When to throw the exception. A
  939. * value of 0 throws immediately.
  940. * A value of 1 actually allows one call
  941. * to this method before throwing. 2
  942. * will allow two calls before throwing
  943. * and so on.
  944. * @param string $method Method name to throw on.
  945. * @param object $exception Exception object to throw.
  946. * If not given then a simple
  947. * Exception object is thrown.
  948. * @param array $args Optional argument list filter.
  949. * If given then the exception
  950. * will only be thrown if the
  951. * method call matches the arguments.
  952. */
  953. function throwAt($timing, $method, $exception = false, $args = false) {
  954. $this->dieOnNoMethod($method, "throw at");
  955. $this->actions->registerAt($timing, $method, $args,
  956. new SimpleThrower($exception ? $exception : new Exception()));
  957. }
  958. /**
  959. * Sets up a trigger to throw an error upon the
  960. * method call.
  961. * @param string $method Method name to throw on.
  962. * @param object $error Error message to trigger.
  963. * @param array $args Optional argument list filter.
  964. * If given then the exception
  965. * will only be thrown if the
  966. * method call matches the arguments.
  967. * @param integer $severity The PHP severity level. Defaults
  968. * to E_USER_ERROR.
  969. */
  970. function errorOn($method, $error = 'A mock error', $args = false, $severity = E_USER_ERROR) {
  971. $this->dieOnNoMethod($method, "error on");
  972. $this->actions->register($method, $args, new SimpleErrorThrower($error, $severity));
  973. }
  974. /**
  975. * Sets up a trigger to throw an error upon a specific
  976. * method call.
  977. * @param integer $timing When to throw the exception. A
  978. * value of 0 throws immediately.
  979. * A value of 1 actually allows one call
  980. * to this method before throwing. 2
  981. * will allow two calls before throwing
  982. * and so on.
  983. * @param string $method Method name to throw on.
  984. * @param object $error Error message to trigger.
  985. * @param array $args Optional argument list filter.
  986. * If given then the exception
  987. * will only be thrown if the
  988. * method call matches the arguments.
  989. * @param integer $severity The PHP severity level. Defaults
  990. * to E_USER_ERROR.
  991. */
  992. function errorAt($timing, $method, $error = 'A mock error', $args = false, $severity = E_USER_ERROR) {
  993. $this->dieOnNoMethod($method, "error at");
  994. $this->actions->registerAt($timing, $method, $args, new SimpleErrorThrower($error, $severity));
  995. }
  996. /**
  997. * Receives event from unit test that the current
  998. * test method has finished. Totals up the call
  999. * counts and triggers a test assertion if a test
  1000. * is present for expected call counts.
  1001. * @param string $test_method Current method name.
  1002. * @param SimpleTestCase $test Test to send message to.
  1003. */
  1004. function atTestEnd($test_method, &$test) {
  1005. foreach ($this->expected_counts as $method => $expectation) {
  1006. $test->assert($expectation, $this->getCallCount($method));
  1007. }
  1008. foreach ($this->max_counts as $method => $expectation) {
  1009. if ($expectation->test($this->getCallCount($method))) {
  1010. $test->assert($expectation, $this->getCallCount($method));
  1011. }
  1012. }
  1013. }
  1014. /**
  1015. * Returns the expected value for the method name
  1016. * and checks expectations. Will generate any
  1017. * test assertions as a result of expectations
  1018. * if there is a test present.
  1019. * @param string $method Name of method to simulate.
  1020. * @param array $args Arguments as an array.
  1021. * @return mixed Stored return.
  1022. */
  1023. function &invoke($method, $args) {
  1024. $method = strtolower($method);
  1025. $step = $this->getCallCount($method);
  1026. $this->addCall($method, $args);
  1027. $this->checkExpectations($method, $args, $step);
  1028. $was = $this->disableEStrict();
  1029. try {
  1030. $result = &$this->emulateCall($method, $args, $step);
  1031. } catch (Exception $e) {
  1032. $this->restoreEStrict($was);
  1033. throw $e;
  1034. }
  1035. $this->restoreEStrict($was);
  1036. return $result;
  1037. }
  1038. /**
  1039. * Finds the return value matching the incoming
  1040. * arguments. If there is no matching value found
  1041. * then an error is triggered.
  1042. * @param string $method Method name.
  1043. * @param array $args Calling arguments.
  1044. * @param integer $step Current position in the
  1045. * call history.
  1046. * @return mixed Stored return or other action.
  1047. */
  1048. protected function &emulateCall($method, $args, $step) {
  1049. return $this->actions->respond($step, $method, $args);
  1050. }
  1051. /**
  1052. * Tests the arguments against expectations.
  1053. * @param string $method Method to check.
  1054. * @param array $args Argument list to match.
  1055. * @param integer $timing The position of this call
  1056. * in the call history.
  1057. */
  1058. protected function checkExpectations($method, $args, $timing) {
  1059. $test = $this->getCurrentTestCase();
  1060. if (isset($this->max_counts[$method])) {
  1061. if (! $this->max_counts[$method]->test($timing + 1)) {
  1062. $test->assert($this->max_counts[$method], $timing + 1);
  1063. }
  1064. }
  1065. if (isset($this->expected_args_at[$timing][$method])) {
  1066. $test->assert(
  1067. $this->expected_args_at[$timing][$method],
  1068. $args,
  1069. "Mock method [$method] at [$timing] -> %s");
  1070. } elseif (isset($this->expected_args[$method])) {
  1071. $test->assert(
  1072. $this->expected_args[$method],
  1073. $args,
  1074. "Mock method [$method] -> %s");
  1075. }
  1076. }
  1077. /**
  1078. * Our mock has to be able to return anything, including
  1079. * variable references. To allow for these mixed returns
  1080. * we have to disable the E_STRICT warnings while the
  1081. * method calls are emulated.
  1082. */
  1083. private function disableEStrict() {
  1084. $was = error_reporting();
  1085. error_reporting($was & ~E_STRICT);
  1086. return $was;
  1087. }
  1088. /**
  1089. * Restores the E_STRICT level if it was previously set.
  1090. * @param integer $was Previous error reporting level.
  1091. */
  1092. private function restoreEStrict($was) {
  1093. error_reporting($was);
  1094. }
  1095. }
  1096. /**
  1097. * Static methods only service class for code generation of
  1098. * mock objects.
  1099. * @package SimpleTest
  1100. * @subpackage MockObjects
  1101. */
  1102. class Mock {
  1103. /**
  1104. * Factory for mock object classes.
  1105. */
  1106. function __construct() {
  1107. trigger_error('Mock factory methods are static.');
  1108. }
  1109. /**
  1110. * Clones a class' interface and creates a mock version
  1111. * that can have return values and expectations set.
  1112. * @param string $class Class to clone.
  1113. * @param string $mock_class New class name. Default is
  1114. * the old name with "Mock"
  1115. * prepended.
  1116. * @param array $methods Additional methods to add beyond
  1117. * those in the cloned class. Use this
  1118. * to emulate the dynamic addition of
  1119. * methods in the cloned class or when
  1120. * the class hasn't been written yet.sta
  1121. */
  1122. static function generate($class, $mock_class = false, $methods = false) {
  1123. $generator = new MockGenerator($class, $mock_class);
  1124. return @$generator->generateSubclass($methods);
  1125. }
  1126. /**
  1127. * Generates a version of a class with selected
  1128. * methods mocked only. Inherits the old class
  1129. * and chains the mock methods of an aggregated
  1130. * mock object.
  1131. * @param string $class Class to clone.
  1132. * @param string $mock_class New class name.
  1133. * @param array $methods Methods to be overridden
  1134. * with mock versions.
  1135. */
  1136. static function generatePartial($class, $mock_class, $methods) {
  1137. $generator = new MockGenerator($class, $mock_class);
  1138. return @$generator->generatePartial($methods);
  1139. }
  1140. /**
  1141. * Uses a stack trace to find the line of an assertion.
  1142. */
  1143. static function getExpectationLine() {
  1144. $trace = new SimpleStackTrace(array('expect'));
  1145. return $trace->traceMethod();
  1146. }
  1147. }
  1148. /**
  1149. * Service class for code generation of mock objects.
  1150. * @package SimpleTest
  1151. * @subpackage MockObjects
  1152. */
  1153. class MockGenerator {
  1154. private $class;
  1155. private $mock_class;
  1156. private $mock_base;
  1157. private $reflection;
  1158. /**
  1159. * Builds initial reflection object.
  1160. * @param string $class Class to be mocked.
  1161. * @param string $mock_class New class with identical interface,
  1162. * but no behaviour.
  1163. */
  1164. function __construct($class, $mock_class) {
  1165. $this->class = $class;
  1166. $this->mock_class = $mock_class;
  1167. if (! $this->mock_class) {
  1168. $this->mock_class = 'Mock' . $this->class;
  1169. }
  1170. $this->mock_base = SimpleTest::getMockBaseClass();
  1171. $this->reflection = new SimpleReflection($this->class);
  1172. }
  1173. /**
  1174. * Clones a class' interface and creates a mock version
  1175. * that can have return values and expectations set.
  1176. * @param array $methods Additional methods to add beyond
  1177. * those in th cloned class. Use this
  1178. * to emulate the dynamic addition of
  1179. * methods in the cloned class or when
  1180. * the class hasn't been written yet.
  1181. */
  1182. function generate($methods) {
  1183. if (! $this->reflection->classOrInterfaceExists()) {
  1184. return false;
  1185. }
  1186. $mock_reflection = new SimpleReflection($this->mock_class);
  1187. if ($mock_reflection->classExistsSansAutoload()) {
  1188. return false;
  1189. }
  1190. $code = $this->createClassCode($methods ? $methods : array());
  1191. return eval("$code return \$code;");
  1192. }
  1193. /**
  1194. * Subclasses a class and overrides every method with a mock one
  1195. * that can have return values and expectations set. Chains
  1196. * to an aggregated SimpleMock.
  1197. * @param array $methods Additional methods to add beyond
  1198. * those in the cloned class. Use this
  1199. * to emulate the dynamic addition of
  1200. * methods in the cloned class or when
  1201. * the class hasn't been written yet.
  1202. */
  1203. function generateSubclass($methods) {
  1204. if (! $this->reflection->classOrInterfaceExists()) {
  1205. return false;
  1206. }
  1207. $mock_reflection = new SimpleReflection($this->mock_class);
  1208. if ($mock_reflection->classExistsSansAutoload()) {
  1209. return false;
  1210. }
  1211. if ($this->reflection->isInterface() || $this->reflection->hasFinal()) {
  1212. $code = $this->createClassCode($methods ? $methods : array());
  1213. return eval("$code return \$code;");
  1214. } else {
  1215. $code = $this->createSubclassCode($methods ? $methods : array());
  1216. return eval("$code return \$code;");
  1217. }
  1218. }
  1219. /**
  1220. * Generates a version of a class with selected
  1221. * methods mocked only. Inherits the old class
  1222. * and chains the mock methods of an aggregated
  1223. * mock object.
  1224. * @param array $methods Methods to be overridden
  1225. * with mock versions.
  1226. */
  1227. function generatePartial($methods) {
  1228. if (! $this->reflection->classExists($this->class)) {
  1229. return false;
  1230. }
  1231. $mock_reflection = new SimpleReflection($this->mock_class);
  1232. if ($mock_reflection->classExistsSansAutoload()) {
  1233. trigger_error('Partial mock class [' . $this->mock_class . '] already exists');
  1234. return false;
  1235. }
  1236. $code = $this->extendClassCode($methods);
  1237. return eval("$code return \$code;");
  1238. }
  1239. /**
  1240. * The new mock class code as a string.
  1241. * @param array $methods Additional methods.
  1242. * @return string Code for new mock class.
  1243. */
  1244. protected function createClassCode($methods) {
  1245. $implements = '';
  1246. $interfaces = $this->reflection->getInterfaces();
  1247. if (function_exists('spl_classes')) {
  1248. $interfaces = array_diff($interfaces, array('Traversable'));
  1249. }
  1250. if (count($interfaces) > 0) {
  1251. $implements = 'implements ' . implode(', ', $interfaces);
  1252. }
  1253. $code = "class " . $this->mock_class . " extends " . $this->mock_base . " $implements {\n";
  1254. $code .= " function " . $this->mock_class . "() {\n";
  1255. $code .= " \$this->" . $this->mock_base . "();\n";
  1256. $code .= " }\n";
  1257. if (in_array('__construct', $this->reflection->getMethods())) {
  1258. $code .= " function __construct() {\n";
  1259. $code .= " \$this->" . $this->mock_base . "();\n";
  1260. $code .= " }\n";
  1261. }
  1262. $code .= $this->createHandlerCode($methods);
  1263. $code .= "}\n";
  1264. return $code;
  1265. }
  1266. /**
  1267. * The new mock class code as a string. The mock will
  1268. * be a subclass of the original mocked class.
  1269. * @param array $methods Additional methods.
  1270. * @return string Code for new mock class.
  1271. */
  1272. protected function createSubclassCode($methods) {
  1273. $code = "class " . $this->mock_class . " extends " . $this->class . " {\n";
  1274. $code .= " public \$mock;\n";
  1275. $code .= $this->addMethodList(array_merge($methods, $this->reflection->getMethods()));
  1276. $cod

Large files files are truncated, but you can click here to view the full file