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