mock_objects.php 61 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641
  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. $code .= "\n";
  1277. $code .= " function " . $this->mock_class . "() {\n";
  1278. $code .= " \$this->mock = new " . $this->mock_base . "();\n";
  1279. $code .= " \$this->mock->disableExpectationNameChecks();\n";
  1280. $code .= " }\n";
  1281. $code .= $this->chainMockReturns();
  1282. $code .= $this->chainMockExpectations();
  1283. $code .= $this->chainThrowMethods();
  1284. $code .= $this->overrideMethods($this->reflection->getMethods());
  1285. $code .= $this->createNewMethodCode($methods);
  1286. $code .= "}\n";
  1287. return $code;
  1288. }
  1289. /**
  1290. * The extension class code as a string. The class
  1291. * composites a mock object and chains mocked methods
  1292. * to it.
  1293. * @param array $methods Mocked methods.
  1294. * @return string Code for a new class.
  1295. */
  1296. protected function extendClassCode($methods) {
  1297. $code = "class " . $this->mock_class . " extends " . $this->class . " {\n";
  1298. $code .= " protected \$mock;\n";
  1299. $code .= $this->addMethodList($methods);
  1300. $code .= "\n";
  1301. $code .= " function " . $this->mock_class . "() {\n";
  1302. $code .= " \$this->mock = new " . $this->mock_base . "();\n";
  1303. $code .= " \$this->mock->disableExpectationNameChecks();\n";
  1304. $code .= " }\n";
  1305. $code .= $this->chainMockReturns();
  1306. $code .= $this->chainMockExpectations();
  1307. $code .= $this->chainThrowMethods();
  1308. $code .= $this->overrideMethods($methods);
  1309. $code .= "}\n";
  1310. return $code;
  1311. }
  1312. /**
  1313. * Creates code within a class to generate replaced
  1314. * methods. All methods call the invoke() handler
  1315. * with the method name and the arguments in an
  1316. * array.
  1317. * @param array $methods Additional methods.
  1318. */
  1319. protected function createHandlerCode($methods) {
  1320. $code = '';
  1321. $methods = array_merge($methods, $this->reflection->getMethods());
  1322. foreach ($methods as $method) {
  1323. if ($this->isConstructor($method)) {
  1324. continue;
  1325. }
  1326. $mock_reflection = new SimpleReflection($this->mock_base);
  1327. if (in_array($method, $mock_reflection->getMethods())) {
  1328. continue;
  1329. }
  1330. $code .= " " . $this->reflection->getSignature($method) . " {\n";
  1331. $code .= " \$args = func_get_args();\n";
  1332. $code .= " \$result = &\$this->invoke(\"$method\", \$args);\n";
  1333. $code .= " return \$result;\n";
  1334. $code .= " }\n";
  1335. }
  1336. return $code;
  1337. }
  1338. /**
  1339. * Creates code within a class to generate a new
  1340. * methods. All methods call the invoke() handler
  1341. * on the internal mock with the method name and
  1342. * the arguments in an array.
  1343. * @param array $methods Additional methods.
  1344. */
  1345. protected function createNewMethodCode($methods) {
  1346. $code = '';
  1347. foreach ($methods as $method) {
  1348. if ($this->isConstructor($method)) {
  1349. continue;
  1350. }
  1351. $mock_reflection = new SimpleReflection($this->mock_base);
  1352. if (in_array($method, $mock_reflection->getMethods())) {
  1353. continue;
  1354. }
  1355. $code .= " " . $this->reflection->getSignature($method) . " {\n";
  1356. $code .= " \$args = func_get_args();\n";
  1357. $code .= " \$result = &\$this->mock->invoke(\"$method\", \$args);\n";
  1358. $code .= " return \$result;\n";
  1359. $code .= " }\n";
  1360. }
  1361. return $code;
  1362. }
  1363. /**
  1364. * Tests to see if a special PHP method is about to
  1365. * be stubbed by mistake.
  1366. * @param string $method Method name.
  1367. * @return boolean True if special.
  1368. */
  1369. protected function isConstructor($method) {
  1370. return in_array(
  1371. strtolower($method),
  1372. array('__construct', '__destruct'));
  1373. }
  1374. /**
  1375. * Creates a list of mocked methods for error checking.
  1376. * @param array $methods Mocked methods.
  1377. * @return string Code for a method list.
  1378. */
  1379. protected function addMethodList($methods) {
  1380. return " protected \$mocked_methods = array('" .
  1381. implode("', '", array_map('strtolower', $methods)) .
  1382. "');\n";
  1383. }
  1384. /**
  1385. * Creates code to abandon the expectation if not mocked.
  1386. * @param string $alias Parameter name of method name.
  1387. * @return string Code for bail out.
  1388. */
  1389. protected function bailOutIfNotMocked($alias) {
  1390. $code = " if (! in_array(strtolower($alias), \$this->mocked_methods)) {\n";
  1391. $code .= " trigger_error(\"Method [$alias] is not mocked\");\n";
  1392. $code .= " \$null = null;\n";
  1393. $code .= " return \$null;\n";
  1394. $code .= " }\n";
  1395. return $code;
  1396. }
  1397. /**
  1398. * Creates source code for chaining to the composited
  1399. * mock object.
  1400. * @return string Code for mock set up.
  1401. */
  1402. protected function chainMockReturns() {
  1403. $code = " function returns(\$method, \$value, \$args = false) {\n";
  1404. $code .= $this->bailOutIfNotMocked("\$method");
  1405. $code .= " \$this->mock->returns(\$method, \$value, \$args);\n";
  1406. $code .= " }\n";
  1407. $code .= " function returnsAt(\$timing, \$method, \$value, \$args = false) {\n";
  1408. $code .= $this->bailOutIfNotMocked("\$method");
  1409. $code .= " \$this->mock->returnsAt(\$timing, \$method, \$value, \$args);\n";
  1410. $code .= " }\n";
  1411. $code .= " function returnsByValue(\$method, \$value, \$args = false) {\n";
  1412. $code .= $this->bailOutIfNotMocked("\$method");
  1413. $code .= " \$this->mock->setReturnValue(\$method, \$value, \$args);\n";
  1414. $code .= " }\n";
  1415. $code .= " function returnsByValueAt(\$timing, \$method, \$value, \$args = false) {\n";
  1416. $code .= $this->bailOutIfNotMocked("\$method");
  1417. $code .= " \$this->mock->setReturnValueAt(\$timing, \$method, \$value, \$args);\n";
  1418. $code .= " }\n";
  1419. $code .= " function returnsByReference(\$method, &\$ref, \$args = false) {\n";
  1420. $code .= $this->bailOutIfNotMocked("\$method");
  1421. $code .= " \$this->mock->setReturnReference(\$method, \$ref, \$args);\n";
  1422. $code .= " }\n";
  1423. $code .= " function returnsByReferenceAt(\$timing, \$method, &\$ref, \$args = false) {\n";
  1424. $code .= $this->bailOutIfNotMocked("\$method");
  1425. $code .= " \$this->mock->setReturnReferenceAt(\$timing, \$method, \$ref, \$args);\n";
  1426. $code .= " }\n";
  1427. $code .= " function setReturnValue(\$method, \$value, \$args = false) {\n";
  1428. $code .= $this->bailOutIfNotMocked("\$method");
  1429. $code .= " \$this->mock->setReturnValue(\$method, \$value, \$args);\n";
  1430. $code .= " }\n";
  1431. $code .= " function setReturnValueAt(\$timing, \$method, \$value, \$args = false) {\n";
  1432. $code .= $this->bailOutIfNotMocked("\$method");
  1433. $code .= " \$this->mock->setReturnValueAt(\$timing, \$method, \$value, \$args);\n";
  1434. $code .= " }\n";
  1435. $code .= " function setReturnReference(\$method, &\$ref, \$args = false) {\n";
  1436. $code .= $this->bailOutIfNotMocked("\$method");
  1437. $code .= " \$this->mock->setReturnReference(\$method, \$ref, \$args);\n";
  1438. $code .= " }\n";
  1439. $code .= " function setReturnReferenceAt(\$timing, \$method, &\$ref, \$args = false) {\n";
  1440. $code .= $this->bailOutIfNotMocked("\$method");
  1441. $code .= " \$this->mock->setReturnReferenceAt(\$timing, \$method, \$ref, \$args);\n";
  1442. $code .= " }\n";
  1443. return $code;
  1444. }
  1445. /**
  1446. * Creates source code for chaining to an aggregated
  1447. * mock object.
  1448. * @return string Code for expectations.
  1449. */
  1450. protected function chainMockExpectations() {
  1451. $code = " function expect(\$method, \$args = false, \$msg = '%s') {\n";
  1452. $code .= $this->bailOutIfNotMocked("\$method");
  1453. $code .= " \$this->mock->expect(\$method, \$args, \$msg);\n";
  1454. $code .= " }\n";
  1455. $code .= " function expectAt(\$timing, \$method, \$args = false, \$msg = '%s') {\n";
  1456. $code .= $this->bailOutIfNotMocked("\$method");
  1457. $code .= " \$this->mock->expectAt(\$timing, \$method, \$args, \$msg);\n";
  1458. $code .= " }\n";
  1459. $code .= " function expectCallCount(\$method, \$count) {\n";
  1460. $code .= $this->bailOutIfNotMocked("\$method");
  1461. $code .= " \$this->mock->expectCallCount(\$method, \$count, \$msg = '%s');\n";
  1462. $code .= " }\n";
  1463. $code .= " function expectMaximumCallCount(\$method, \$count, \$msg = '%s') {\n";
  1464. $code .= $this->bailOutIfNotMocked("\$method");
  1465. $code .= " \$this->mock->expectMaximumCallCount(\$method, \$count, \$msg = '%s');\n";
  1466. $code .= " }\n";
  1467. $code .= " function expectMinimumCallCount(\$method, \$count, \$msg = '%s') {\n";
  1468. $code .= $this->bailOutIfNotMocked("\$method");
  1469. $code .= " \$this->mock->expectMinimumCallCount(\$method, \$count, \$msg = '%s');\n";
  1470. $code .= " }\n";
  1471. $code .= " function expectNever(\$method) {\n";
  1472. $code .= $this->bailOutIfNotMocked("\$method");
  1473. $code .= " \$this->mock->expectNever(\$method);\n";
  1474. $code .= " }\n";
  1475. $code .= " function expectOnce(\$method, \$args = false, \$msg = '%s') {\n";
  1476. $code .= $this->bailOutIfNotMocked("\$method");
  1477. $code .= " \$this->mock->expectOnce(\$method, \$args, \$msg);\n";
  1478. $code .= " }\n";
  1479. $code .= " function expectAtLeastOnce(\$method, \$args = false, \$msg = '%s') {\n";
  1480. $code .= $this->bailOutIfNotMocked("\$method");
  1481. $code .= " \$this->mock->expectAtLeastOnce(\$method, \$args, \$msg);\n";
  1482. $code .= " }\n";
  1483. return $code;
  1484. }
  1485. /**
  1486. * Adds code for chaining the throw methods.
  1487. * @return string Code for chains.
  1488. */
  1489. protected function chainThrowMethods() {
  1490. $code = " function throwOn(\$method, \$exception = false, \$args = false) {\n";
  1491. $code .= $this->bailOutIfNotMocked("\$method");
  1492. $code .= " \$this->mock->throwOn(\$method, \$exception, \$args);\n";
  1493. $code .= " }\n";
  1494. $code .= " function throwAt(\$timing, \$method, \$exception = false, \$args = false) {\n";
  1495. $code .= $this->bailOutIfNotMocked("\$method");
  1496. $code .= " \$this->mock->throwAt(\$timing, \$method, \$exception, \$args);\n";
  1497. $code .= " }\n";
  1498. $code .= " function errorOn(\$method, \$error = 'A mock error', \$args = false, \$severity = E_USER_ERROR) {\n";
  1499. $code .= $this->bailOutIfNotMocked("\$method");
  1500. $code .= " \$this->mock->errorOn(\$method, \$error, \$args, \$severity);\n";
  1501. $code .= " }\n";
  1502. $code .= " function errorAt(\$timing, \$method, \$error = 'A mock error', \$args = false, \$severity = E_USER_ERROR) {\n";
  1503. $code .= $this->bailOutIfNotMocked("\$method");
  1504. $code .= " \$this->mock->errorAt(\$timing, \$method, \$error, \$args, \$severity);\n";
  1505. $code .= " }\n";
  1506. return $code;
  1507. }
  1508. /**
  1509. * Creates source code to override a list of methods
  1510. * with mock versions.
  1511. * @param array $methods Methods to be overridden
  1512. * with mock versions.
  1513. * @return string Code for overridden chains.
  1514. */
  1515. protected function overrideMethods($methods) {
  1516. $code = "";
  1517. foreach ($methods as $method) {
  1518. if ($this->isConstructor($method)) {
  1519. continue;
  1520. }
  1521. $code .= " " . $this->reflection->getSignature($method) . " {\n";
  1522. $code .= " \$args = func_get_args();\n";
  1523. $code .= " \$result = &\$this->mock->invoke(\"$method\", \$args);\n";
  1524. $code .= " return \$result;\n";
  1525. $code .= " }\n";
  1526. }
  1527. return $code;
  1528. }
  1529. }
  1530. ?>