From af68030fcf0c266300feb2c100149ecadef7d364 Mon Sep 17 00:00:00 2001 From: xue <> Date: Sun, 16 Jul 2006 01:50:23 +0000 Subject: Merge from 3.0 branch till 1264. --- tests/test_tools/simpletest/mock_objects.php | 832 +++++++++++++-------------- 1 file changed, 391 insertions(+), 441 deletions(-) (limited to 'tests/test_tools/simpletest/mock_objects.php') diff --git a/tests/test_tools/simpletest/mock_objects.php b/tests/test_tools/simpletest/mock_objects.php index d39eb84e..0b003491 100644 --- a/tests/test_tools/simpletest/mock_objects.php +++ b/tests/test_tools/simpletest/mock_objects.php @@ -3,37 +3,36 @@ * base include file for SimpleTest * @package SimpleTest * @subpackage MockObjects - * @version $Id: mock_objects.php,v 1.61 2005/02/13 01:09:25 lastcraft Exp $ + * @version $Id: mock_objects.php,v 1.90 2006/02/05 19:35:31 lastcraft Exp $ */ /**#@+ * include SimpleTest files */ require_once(dirname(__FILE__) . '/expectation.php'); - require_once(dirname(__FILE__) . '/options.php'); + require_once(dirname(__FILE__) . '/simpletest.php'); require_once(dirname(__FILE__) . '/dumper.php'); + if (version_compare(phpversion(), '5') >= 0) { + require_once(dirname(__FILE__) . '/reflection_php5.php'); + } else { + require_once(dirname(__FILE__) . '/reflection_php4.php'); + } /**#@-*/ - + /** * Default character simpletest will substitute for any value */ - define('MOCK_WILDCARD', '*'); - + if (! defined('MOCK_ANYTHING')) { + define('MOCK_ANYTHING', '*'); + } + /** * A wildcard expectation always matches. * @package SimpleTest * @subpackage MockObjects */ - class WildcardExpectation extends SimpleExpectation { - - /** - * Chains constructor only. - * @access public - */ - function WildcardExpectation() { - $this->SimpleExpectation(); - } - + class AnythingExpectation extends SimpleExpectation { + /** * Tests the expectation. Always true. * @param mixed $compare Ignored. @@ -43,7 +42,7 @@ function test($compare) { return true; } - + /** * Returns a human readable test message. * @param mixed $compare Comparison value. @@ -53,10 +52,10 @@ */ function testMessage($compare) { $dumper = $this->_getDumper(); - return 'Wildcard always matches [' . $dumper->describeValue($compare) . ']'; + return 'Anything always matches [' . $dumper->describeValue($compare) . ']'; } } - + /** * Parameter comparison assertion. * @package SimpleTest @@ -64,7 +63,7 @@ */ class ParametersExpectation extends SimpleExpectation { protected $_expected; - + /** * Sets the expected parameter list. * @param array $parameters Array of parameters including @@ -80,7 +79,7 @@ $this->SimpleExpectation($message); $this->_expected = $expected; } - + /** * Tests the assertion. True if correct. * @param array $parameters Comparison values. @@ -101,7 +100,7 @@ } return true; } - + /** * Tests an individual parameter. * @param mixed $parameter Value to test. @@ -114,7 +113,7 @@ $comparison = $this->_coerceToExpectation($expected); return $comparison->test($parameter); } - + /** * Returns a human readable test message. * @param array $comparison Incoming parameter list. @@ -131,7 +130,7 @@ return $this->_describeDifference($this->_expected, $parameters); } } - + /** * Message to display if expectation differs from * the parameters actually received. @@ -157,7 +156,7 @@ } return "Parameter expectation differs at " . implode(" and ", $messages); } - + /** * Creates an identical expectation if the * object/value is not already some type @@ -167,12 +166,12 @@ * @access private */ function _coerceToExpectation($expected) { - if (SimpleTestCompatibility::isA($expected, 'SimpleExpectation')) { + if (SimpleExpectation::isExpectation($expected)) { return $expected; } return new IdenticalExpectation($expected); } - + /** * Renders the argument list as a string for * messages. @@ -191,14 +190,14 @@ return implode(', ', $descriptions); } } - + /** * Confirms that the number of calls on a method is as expected. */ class CallCountExpectation extends SimpleExpectation { protected $_method; protected $_count; - + /** * Stashes the method and expected count for later * reporting. @@ -211,7 +210,7 @@ $this->_count = $count; $this->SimpleExpectation($message); } - + /** * Tests the assertion. True if correct. * @param integer $compare Measured call count. @@ -221,7 +220,7 @@ function test($compare) { return ($this->_count == $compare); } - + /** * Reports the comparison. * @param integer $compare Measured call count. @@ -234,14 +233,14 @@ '] got [' . $compare . ']'; } } - + /** * Confirms that the number of calls on a method is as expected. */ class MinimumCallCountExpectation extends SimpleExpectation { protected $_method; protected $_count; - + /** * Stashes the method and expected count for later * reporting. @@ -254,7 +253,7 @@ $this->_count = $count; $this->SimpleExpectation($message); } - + /** * Tests the assertion. True if correct. * @param integer $compare Measured call count. @@ -264,7 +263,7 @@ function test($compare) { return ($this->_count <= $compare); } - + /** * Reports the comparison. * @param integer $compare Measured call count. @@ -277,14 +276,14 @@ '] got [' . $compare . ']'; } } - + /** * Confirms that the number of calls on a method is as expected. */ class MaximumCallCountExpectation extends SimpleExpectation { protected $_method; protected $_count; - + /** * Stashes the method and expected count for later * reporting. @@ -297,7 +296,7 @@ $this->_count = $count; $this->SimpleExpectation($message); } - + /** * Tests the assertion. True if correct. * @param integer $compare Measured call count. @@ -307,7 +306,7 @@ function test($compare) { return ($this->_count >= $compare); } - + /** * Reports the comparison. * @param integer $compare Measured call count. @@ -329,7 +328,7 @@ */ class CallMap { protected $_map; - + /** * Creates an empty call map. * @access public @@ -337,7 +336,7 @@ function CallMap() { $this->_map = array(); } - + /** * Stashes a value against a method call. * @param array $parameters Arguments including wildcards. @@ -347,7 +346,7 @@ function addValue($parameters, $value) { $this->addReference($parameters, $value); } - + /** * Stashes a reference against a method call. * @param array $parameters Array of arguments (including wildcards). @@ -360,7 +359,7 @@ $this->_map[$place]["params"] = new ParametersExpectation($parameters); $this->_map[$place]["content"] = $reference; } - + /** * Searches the call list for a matching parameter * set. Returned by reference. @@ -370,14 +369,15 @@ * slot, otherwise null. * @access public */ - function findFirstMatch($parameters) { + function &findFirstMatch($parameters) { $slot = $this->_findFirstSlot($parameters); if (!isset($slot)) { - return null; + $null = null; + return $null; } return $slot["content"]; } - + /** * Searches the call list for a matching parameter * set. True if successful. @@ -389,7 +389,7 @@ function isMatch($parameters) { return ($this->_findFirstSlot($parameters) != null); } - + /** * Searches the map for a matching item. * @param array $parameters Parameters to search by @@ -397,78 +397,100 @@ * @return array Reference to slot or null. * @access private */ - function _findFirstSlot($parameters) { + function &_findFirstSlot($parameters) { $count = count($this->_map); for ($i = 0; $i < $count; $i++) { if ($this->_map[$i]["params"]->test($parameters)) { return $this->_map[$i]; } } - return null; + $null = null; + return $null; } } - + /** * An empty collection of methods that can have their - * return values set. Used for prototyping. + * return values set and expectations made of the + * calls upon them. The mock will assert the + * expectations against it's attached test case in + * addition to the server stub behaviour. * @package SimpleTest * @subpackage MockObjects */ - class SimpleStub { - protected $_wildcard; - protected $_is_strict; + class SimpleMock { + protected $_wildcard = MOCK_ANYTHING; + protected $_is_strict = true; protected $_returns; protected $_return_sequence; protected $_call_counts; - + protected $_expected_counts; + protected $_max_counts; + protected $_expected_args; + protected $_expected_args_at; + /** - * Sets up the wildcard and everything else empty. - * @param mixed $wildcard Parameter matching wildcard. - * @param boolean $is_strict Enables method name checks. - * @access public + * Creates an empty return list and expectation list. + * All call counts are set to zero. + * @param SimpleTestCase $test Test case to test expectations in. + * @param mixed $wildcard Parameter matching wildcard. + * @param boolean $is_strict Enables method name checks on + * expectations. */ - function SimpleStub($wildcard, $is_strict = true) { - $this->_wildcard = $wildcard; - $this->_is_strict = $is_strict; + function SimpleMock() { $this->_returns = array(); $this->_return_sequence = array(); $this->_call_counts = array(); + $test = $this->_getCurrentTestCase(); + $test->tell($this); + $this->_expected_counts = array(); + $this->_max_counts = array(); + $this->_expected_args = array(); + $this->_expected_args_at = array(); } - + /** - * Replaces wildcard matches with wildcard - * expectations in the argument list. - * @param array $args Raw argument list. - * @return array Argument list with - * expectations. - * @access private + * Disables a name check when setting expectations. + * This hack is needed for the partial mocks. + * @access public */ - function _replaceWildcards($args) { - if ($args === false) { - return false; - } - for ($i = 0; $i < count($args); $i++) { - if ($args[$i] === $this->_wildcard) { - $args[$i] = new WildcardExpectation(); - } - } - return $args; + function disableExpectationNameChecks() { + $this->_is_strict = false; } - + /** - * Returns the expected value for the method name. - * @param string $method Name of method to simulate. - * @param array $args Arguments as an array. - * @return mixed Stored return. + * Changes the default wildcard object. + * @param mixed $wildcard Parameter matching wildcard. + * @access public + */ + function setWildcard($wildcard) { + $this->_wildcard = $wildcard; + } + + /** + * Finds currently running test. + * @return SimpeTestCase Current test case. + * @access protected + */ + function &_getCurrentTestCase() { + return SimpleTest::getCurrent(); + } + + /** + * Die if bad arguments array is passed + * @param mixed $args The arguments value to be checked. + * @param string $task Description of task attempt. + * @return boolean Valid arguments * @access private */ - function _invoke($method, $args) { - $method = strtolower($method); - $step = $this->getCallCount($method); - $this->_addCall($method, $args); - return $this->_getReturn($method, $args, $step); + function _checkArgumentsIsArray($args, $task) { + if (! is_array($args)) { + trigger_error( + "Cannot $task as \$args parameter is not an array", + E_USER_ERROR); + } } - + /** * Triggers a PHP error if the method is not part * of this object. @@ -477,13 +499,33 @@ * @access protected */ function _dieOnNoMethod($method, $task) { - if ($this->_is_strict && !method_exists($this, $method)) { + if ($this->_is_strict && ! method_exists($this, $method)) { trigger_error( "Cannot $task as no ${method}() in class " . get_class($this), E_USER_ERROR); } } - + + /** + * Replaces wildcard matches with wildcard + * expectations in the argument list. + * @param array $args Raw argument list. + * @return array Argument list with + * expectations. + * @access private + */ + function _replaceWildcards($args) { + if ($args === false) { + return false; + } + for ($i = 0; $i < count($args); $i++) { + if ($args[$i] === $this->_wildcard) { + $args[$i] = new AnythingExpectation(); + } + } + return $args; + } + /** * Adds one to the call count of a method. * @param string $method Method called. @@ -496,7 +538,7 @@ } $this->_call_counts[$method]++; } - + /** * Fetches the call count of a method so far. * @param string $method Method name called. @@ -511,7 +553,7 @@ } return $this->_call_counts[$method]; } - + /** * Sets a return for a parameter list that will * be passed by value for all calls to this method. @@ -530,7 +572,7 @@ } $this->_returns[$method]->addValue($args, $value); } - + /** * Sets a return for a parameter list that will * be passed by value only when the required call count @@ -557,7 +599,7 @@ } $this->_return_sequence[$method][$timing]->addValue($args, $value); } - + /** * Sets a return for a parameter list that will * be passed by reference for all calls. @@ -576,7 +618,7 @@ } $this->_returns[$method]->addReference($args, $reference); } - + /** * Sets a return for a parameter list that will * be passed by value only when the required call count @@ -603,94 +645,7 @@ } $this->_return_sequence[$method][$timing]->addReference($args, $reference); } - - /** - * Finds the return value matching the incoming - * arguments. If there is no matching value found - * then an error is triggered. - * @param string $method Method name. - * @param array $args Calling arguments. - * @param integer $step Current position in the - * call history. - * @return mixed Stored return. - * @access protected - */ - function _getReturn($method, $args, $step) { - if (isset($this->_return_sequence[$method][$step])) { - if ($this->_return_sequence[$method][$step]->isMatch($args)) { - return $this->_return_sequence[$method][$step]->findFirstMatch($args); - } - } - if (isset($this->_returns[$method])) { - return $this->_returns[$method]->findFirstMatch($args); - } - return null; - } - } - - /** - * An empty collection of methods that can have their - * return values set and expectations made of the - * calls upon them. The mock will assert the - * expectations against it's attached test case in - * addition to the server stub behaviour. - * @package SimpleTest - * @subpackage MockObjects - */ - class SimpleMock extends SimpleStub { - protected $_test; - protected $_expected_counts; - protected $_max_counts; - protected $_expected_args; - protected $_expected_args_at; - - /** - * Creates an empty return list and expectation list. - * All call counts are set to zero. - * @param SimpleTestCase $test Test case to test expectations in. - * @param mixed $wildcard Parameter matching wildcard. - * @param boolean $is_strict Enables method name checks on - * expectations. - * @access public - */ - function SimpleMock($test, $wildcard, $is_strict = true) { - $this->SimpleStub($wildcard, $is_strict); - if (! $test) { - trigger_error('No unit tester for mock object', E_USER_ERROR); - return; - } - $this->_test = SimpleMock::registerTest($test); - $this->_expected_counts = array(); - $this->_max_counts = array(); - $this->_expected_args = array(); - $this->_expected_args_at = array(); - } - - /** - * Accessor for attached unit test so that when - * subclassed, new expectations can be added easily. - * @return SimpleTestCase Unit test passed in constructor. - * @access public - */ - function getTest() { - return $this->_test; - } - - /** - * Die if bad arguments array is passed - * @param mixed $args The arguments value to be checked. - * @param string $task Description of task attempt. - * @return boolean Valid arguments - * @access private - */ - function _checkArgumentsIsArray($args, $task) { - if (! is_array($args)) { - trigger_error( - "Cannot $task as \$args parameter is not an array", - E_USER_ERROR); - } - } - + /** * Sets up an expected call with a set of * expected parameters in that call. All @@ -702,15 +657,22 @@ * @param string $message Overridden message. * @access public */ - function expectArguments($method, $args, $message = '%s') { + function expect($method, $args, $message = '%s') { $this->_dieOnNoMethod($method, 'set expected arguments'); $this->_checkArgumentsIsArray($args, 'set expected arguments'); $args = $this->_replaceWildcards($args); - $message .= Mock::getExpectationLine(' at line [%d]'); + $message .= Mock::getExpectationLine(); $this->_expected_args[strtolower($method)] = new ParametersExpectation($args, $message); } - + + /** + * @deprecated + */ + function expectArguments($method, $args, $message = '%s') { + return $this->expect($method, $args, $message); + } + /** * Sets up an expected call with a set of * expected parameters in that call. The @@ -724,7 +686,7 @@ * @param string $message Overridden message. * @access public */ - function expectArgumentsAt($timing, $method, $args, $message = '%s') { + function expectAt($timing, $method, $args, $message = '%s') { $this->_dieOnNoMethod($method, 'set expected arguments at time'); $this->_checkArgumentsIsArray($args, 'set expected arguments at time'); $args = $this->_replaceWildcards($args); @@ -732,11 +694,18 @@ $this->_expected_args_at[$timing] = array(); } $method = strtolower($method); - $message .= Mock::getExpectationLine(' at line [%d]'); + $message .= Mock::getExpectationLine(); $this->_expected_args_at[$timing][$method] = new ParametersExpectation($args, $message); } - + + /** + * @deprecated + */ + function expectArgumentsAt($timing, $method, $args, $message = '%s') { + return $this->expectAt($timing, $method, $args, $message); + } + /** * Sets an expectation for the number of times * a method will be called. The tally method @@ -749,11 +718,11 @@ */ function expectCallCount($method, $count, $message = '%s') { $this->_dieOnNoMethod($method, 'set expected call count'); - $message .= Mock::getExpectationLine(' at line [%d]'); + $message .= Mock::getExpectationLine(); $this->_expected_counts[strtolower($method)] = new CallCountExpectation($method, $count, $message); } - + /** * Sets the number of times a method may be called * before a test failure is triggered. @@ -765,11 +734,11 @@ */ function expectMaximumCallCount($method, $count, $message = '%s') { $this->_dieOnNoMethod($method, 'set maximum call count'); - $message .= Mock::getExpectationLine(' at line [%d]'); - $this->_max_counts[strtolower($method)] = + $message .= Mock::getExpectationLine(); + $this->_max_counts[strtolower($method)] = new MaximumCallCountExpectation($method, $count, $message); } - + /** * Sets the number of times to call a method to prevent * a failure on the tally. @@ -781,11 +750,11 @@ */ function expectMinimumCallCount($method, $count, $message = '%s') { $this->_dieOnNoMethod($method, 'set minimum call count'); - $message .= Mock::getExpectationLine(' at line [%d]'); + $message .= Mock::getExpectationLine(); $this->_expected_counts[strtolower($method)] = new MinimumCallCountExpectation($method, $count, $message); } - + /** * Convenience method for barring a method * call. @@ -796,7 +765,7 @@ function expectNever($method, $message = '%s') { $this->expectMaximumCallCount($method, 0, $message); } - + /** * Convenience method for a single method * call. @@ -812,7 +781,7 @@ $this->expectArguments($method, $args, $message); } } - + /** * Convenience method for requiring a method * call. @@ -828,16 +797,22 @@ $this->expectArguments($method, $args, $message); } } - + /** - * Totals up the call counts and triggers a test - * assertion if a test is present for expected - * call counts. - * This method must be called explicitly for the call - * count assertions to be triggered. - * @access public + * @deprecated */ function tally() { + } + + /** + * Receives event from unit test that the current + * test method has finished. Totals up the call + * counts and triggers a test assertion if a test + * is present for expected call counts. + * @param string $method Current method name. + * @access public + */ + function atTestEnd($method) { foreach ($this->_expected_counts as $method => $expectation) { $this->_assertTrue( $expectation->test($this->getCallCount($method)), @@ -862,14 +837,40 @@ * @return mixed Stored return. * @access private */ - function _invoke($method, $args) { + function &_invoke($method, $args) { $method = strtolower($method); $step = $this->getCallCount($method); $this->_addCall($method, $args); $this->_checkExpectations($method, $args, $step); - return $this->_getReturn($method, $args, $step); + $result = $this->_getReturn($method, $args, $step); + return $result; + } + /** + * Finds the return value matching the incoming + * arguments. If there is no matching value found + * then an error is triggered. + * @param string $method Method name. + * @param array $args Calling arguments. + * @param integer $step Current position in the + * call history. + * @return mixed Stored return. + * @access protected + */ + function &_getReturn($method, $args, $step) { + if (isset($this->_return_sequence[$method][$step])) { + if ($this->_return_sequence[$method][$step]->isMatch($args)) { + $result = $this->_return_sequence[$method][$step]->findFirstMatch($args); + return $result; + } + } + if (isset($this->_returns[$method])) { + $result = $this->_returns[$method]->findFirstMatch($args); + return $result; + } + $null = null; + return $null; } - + /** * Tests the arguments against expectations. * @param string $method Method to check. @@ -897,7 +898,7 @@ "Mock method [$method] -> " . $this->_expected_args[$method]->overlayMessage($args)); } } - + /** * Triggers an assertion on the held test case. * Should be overridden when using another test @@ -909,253 +910,173 @@ * @access protected */ function _assertTrue($assertion, $message) { - $test =SimpleMock::injectTest($this->_test); + $test = $this->_getCurrentTestCase(); $test->assertTrue($assertion, $message); } - - /** - * Stashes the test case for later recovery. - * @param SimpleTestCase $test Test case. - * @return string Key to find it again. - * @access public - * @static - */ - function registerTest($test) { - $registry =SimpleMock::_getRegistry(); - $registry[$class = get_class($test)] = $test; - return $class; - } - - /** - * Resolves the dependency on the test case. - * @param string $class Key to look up test case in. - * @return SimpleTestCase Test case to send results to. - * @access public - * @static - */ - function injectTest($key) { - $registry =SimpleMock::_getRegistry(); - return $registry[$key]; - } - - /** - * Registry for test cases. The reason for this is - * to break the reference between the test cases and - * the mocks. It was leading to a fatal error due to - * recursive dependencies during comparisons. See - * http://bugs.php.net/bug.php?id=31449 for the PHP - * bug. - * @return array List of references. - * @access private - * @static - */ - function _getRegistry() { - static $registry; - if (! isset($registry)) { - $registry = array(); - } - return $registry; - } } - + /** * Static methods only service class for code generation of - * server stubs. + * mock objects. * @package SimpleTest * @subpackage MockObjects */ - class Stub { - - /** - * Factory for server stub classes. - */ - function Stub() { - trigger_error('Stub factory methods are class only.'); - } - - /** - * Clones a class' interface and creates a stub version - * that can have return values set. - * @param string $class Class to clone. - * @param string $stub_class New class name. Default is - * the old name with "Stub" - * prepended. - * @param array $methods Additional methods to add beyond - * those in the cloned class. Use this - * to emulate the dynamic addition of - * methods in the cloned class or when - * the class hasn't been written yet. - * @static + class Mock { + + /** + * Factory for mock object classes. * @access public */ - function generate($class, $stub_class = false, $methods = false) { - if (! SimpleTestCompatibility::classExists($class)) { - return false; - } - if (! $stub_class) { - $stub_class = "Stub" . $class; - } - if (SimpleTestCompatibility::classExists($stub_class)) { - return false; - } - return eval(Stub::_createClassCode( - $class, - $stub_class, - $methods ? $methods : array()) . " return true;"); + function Mock() { + trigger_error('Mock factory methods are class only.'); } - + /** - * The new server stub class code in string form. - * @param string $class Class to clone. - * @param string $mock_class New class name. - * @param array $methods Additional methods. + * Clones a class' interface and creates a mock version + * that can have return values and expectations set. + * @param string $class Class to clone. + * @param string $mock_class New class name. Default is + * the old name with "Mock" + * prepended. + * @param array $methods Additional methods to add beyond + * those in th cloned class. Use this + * to emulate the dynamic addition of + * methods in the cloned class or when + * the class hasn't been written yet. * @static - * @access private + * @access public */ - function _createClassCode($class, $stub_class, $methods) { - $stub_base = SimpleTestOptions::getStubBaseClass(); - $code = "class $stub_class extends $stub_base {\n"; - $code .= " function $stub_class(\$wildcard = MOCK_WILDCARD) {\n"; - $code .= " \$this->$stub_base(\$wildcard);\n"; - $code .= " }\n"; - $code .= Stub::_createHandlerCode($class, $stub_base, $methods); - $code .= "}\n"; - return $code; + function generate($class, $mock_class = false, $methods = false) { + $generator = new MockGenerator($class, $mock_class); + return $generator->generate($methods); } - + /** - * Creates code within a class to generate replaced - * methods. All methods call the _invoke() handler - * with the method name and the arguments in an - * array. - * @param string $class Class to clone. - * @param string $base Base mock/stub class with methods that - * cannot be cloned. Otherwise you - * would be stubbing the accessors used - * to set the stubs. - * @param array $methods Additional methods. + * Generates a version of a class with selected + * methods mocked only. Inherits the old class + * and chains the mock methods of an aggregated + * mock object. + * @param string $class Class to clone. + * @param string $mock_class New class name. + * @param array $methods Methods to be overridden + * with mock versions. * @static - * @access private + * @access public */ - function _createHandlerCode($class, $base, $methods) { - $code = ""; - $methods = array_merge($methods, get_class_methods($class)); - foreach ($methods as $method) { - if (Stub::_isSpecialMethod($method)) { - continue; - } - if (in_array($method, get_class_methods($base))) { - continue; - } - $code .= " function $method() {\n"; - $code .= " \$args = func_get_args();\n"; - $code .= " return \$this->_invoke(\"$method\", \$args);\n"; - $code .= " }\n"; - } - return $code; + function generatePartial($class, $mock_class, $methods) { + $generator = new MockGenerator($class, $mock_class); + return $generator->generatePartial($methods); } - + /** - * Tests to see if a special PHP method is about to - * be stubbed by mistake. - * @param string $method Method name. - * @return boolean True if special. - * @access private + * Uses a stack trace to find the line of an assertion. + * @param array $stack Stack frames top most first. Only + * needed if not using the PHP + * backtrace function. + * @return string Location of first expect* + * method embedded in format string. + * @access public * @static */ - function _isSpecialMethod($method) { - return in_array( - strtolower($method), - array('__construct', '__clone', '__get', '__set', '__call')); + function getExpectationLine($stack = false) { + if ($stack === false) { + $stack = SimpleTestCompatibility::getStackTrace(); + } + return SimpleDumper::getFormattedAssertionLine($stack); } } - + /** - * Static methods only service class for code generation of - * mock objects. + * @deprecated + */ + class Stub extends Mock { + } + + /** + * Service class for code generation of mock objects. * @package SimpleTest * @subpackage MockObjects */ - class Mock { - - /** - * Factory for mock object classes. - * @access public - */ - function Mock() { - trigger_error("Mock factory methods are class only."); + class MockGenerator { + protected $_class; + protected $_mock_class; + protected $_mock_base; + protected $_reflection; + + function MockGenerator($class, $mock_class) { + $this->_class = $class; + $this->_mock_class = $mock_class; + $this->_mock_base = SimpleTest::getMockBaseClass(); + $this->_reflection = new SimpleReflection($this->_class); } - + /** * Clones a class' interface and creates a mock version * that can have return values and expectations set. - * @param string $class Class to clone. - * @param string $mock_class New class name. Default is - * the old name with "Mock" - * prepended. * @param array $methods Additional methods to add beyond * those in th cloned class. Use this * to emulate the dynamic addition of * methods in the cloned class or when * the class hasn't been written yet. - * @static * @access public */ - function generate($class, $mock_class = false, $methods = false) { - if (! SimpleTestCompatibility::classExists($class)) { + function generate($methods) { + if (! $this->_reflection->classOrInterfaceExists()) { return false; } - if (! $mock_class) { - $mock_class = "Mock" . $class; + if (! $this->_mock_class) { + $this->_mock_class = 'Mock' . $this->_class; } - if (SimpleTestCompatibility::classExists($mock_class)) { + $mock_reflection = new SimpleReflection($this->_mock_class); + if ($mock_reflection->classExistsSansAutoload()) { return false; } - return eval(Mock::_createClassCode( - $class, - $mock_class, - $methods ? $methods : array()) . " return true;"); + return eval( + $this->_createClassCode($methods ? $methods : array()) . + " return true;"); } - + /** * Generates a version of a class with selected * methods mocked only. Inherits the old class * and chains the mock methods of an aggregated * mock object. - * @param string $class Class to clone. - * @param string $mock_class New class name. * @param array $methods Methods to be overridden * with mock versions. - * @static * @access public */ - function generatePartial($class, $mock_class, $methods) { - if (! SimpleTestCompatibility::classExists($class)) { + function generatePartial($methods) { + if (! $this->_reflection->classExists($this->_class)) { return false; } - if (SimpleTestCompatibility::classExists($mock_class)) { + $mock_reflection = new SimpleReflection($this->_mock_class); + if ($mock_reflection->classExistsSansAutoload()) { trigger_error("Partial mock class [$mock_class] already exists"); return false; } - return eval(Mock::_extendClassCode($class, $mock_class, $methods)); + return eval($this->_extendClassCode($methods)); } /** * The new mock class code as a string. - * @param string $class Class to clone. - * @param string $mock_class New class name. * @param array $methods Additional methods. * @return string Code for new mock class. - * @static * @access private */ - function _createClassCode($class, $mock_class, $methods) { - $mock_base = SimpleTestOptions::getMockBaseClass(); - $code = "class $mock_class extends $mock_base {\n"; - $code .= " function $mock_class(&\$test, \$wildcard = MOCK_WILDCARD) {\n"; - $code .= " \$this->$mock_base(\$test, \$wildcard);\n"; + function _createClassCode($methods) { + $implements = ''; + $interfaces = $this->_reflection->getInterfaces(); + if (function_exists('spl_classes')) { + $interfaces = array_diff($interfaces, array('Traversable')); + } + if (count($interfaces) > 0) { + $implements = 'implements ' . implode(', ', $interfaces); + } + $code = "class " . $this->_mock_class . " extends " . $this->_mock_base . " $implements {\n"; + $code .= " function " . $this->_mock_class . "() {\n"; + $code .= " \$this->" . $this->_mock_base . "();\n"; $code .= " }\n"; - $code .= Stub::_createHandlerCode($class, $mock_base, $methods); + $code .= $this->_createHandlerCode($methods); $code .= "}\n"; return $code; } @@ -1164,30 +1085,67 @@ * The extension class code as a string. The class * composites a mock object and chains mocked methods * to it. - * @param string $class Class to extend. - * @param string $mock_class New class name. * @param array $methods Mocked methods. * @return string Code for a new class. - * @static * @access private */ - function _extendClassCode($class, $mock_class, $methods) { - $mock_base = SimpleTestOptions::getMockBaseClass(); - $code = "class $mock_class extends $class {\n"; + function _extendClassCode($methods) { + $code = "class " . $this->_mock_class . " extends " . $this->_class . " {\n"; $code .= " var \$_mock;\n"; - $code .= Mock::_addMethodList($methods); + $code .= $this->_addMethodList($methods); $code .= "\n"; - $code .= " function $mock_class(&\$test, \$wildcard = MOCK_WILDCARD) {\n"; - $code .= " \$this->_mock = new $mock_base(\$test, \$wildcard, false);\n"; + $code .= " function " . $this->_mock_class . "() {\n"; + $code .= " \$this->_mock = new " . $this->_mock_base . "();\n"; + $code .= " \$this->_mock->disableExpectationNameChecks();\n"; $code .= " }\n"; - $code .= Mock::_chainMockReturns(); - $code .= Mock::_chainMockExpectations(); - $code .= Mock::_overrideMethods($methods); - $code .= SimpleTestOptions::getPartialMockCode(); + $code .= $this->_chainMockReturns(); + $code .= $this->_chainMockExpectations(); + $code .= $this->_overrideMethods($methods); $code .= "}\n"; return $code; } - + + /** + * Creates code within a class to generate replaced + * methods. All methods call the _invoke() handler + * with the method name and the arguments in an + * array. + * @param array $methods Additional methods. + * @access private + */ + function _createHandlerCode($methods) { + $code = ''; + $methods = array_merge($methods, $this->_reflection->getMethods()); + foreach ($methods as $method) { + if ($this->_isConstructor($method)) { + continue; + } + $mock_reflection = new SimpleReflection($this->_mock_base); + if (in_array($method, $mock_reflection->getMethods())) { + continue; + } + $code .= " " . $this->_reflection->getSignature($method) . " {\n"; + $code .= " \$args = func_get_args();\n"; + $code .= " \$result = &\$this->_invoke(\"$method\", \$args);\n"; + $code .= " return \$result;\n"; + $code .= " }\n"; + } + return $code; + } + + /** + * Tests to see if a special PHP method is about to + * be stubbed by mistake. + * @param string $method Method name. + * @return boolean True if special. + * @access private + */ + function _isConstructor($method) { + return in_array( + strtolower($method), + array('__construct', '__destruct', '__clone')); + } + /** * Creates a list of mocked methods for error checking. * @param array $methods Mocked methods. @@ -1197,7 +1155,7 @@ function _addMethodList($methods) { return " var \$_mocked_methods = array('" . implode("', '", $methods) . "');\n"; } - + /** * Creates code to abandon the expectation if not mocked. * @param string $alias Parameter name of method name. @@ -1207,11 +1165,12 @@ function _bailOutIfNotMocked($alias) { $code = " if (! in_array($alias, \$this->_mocked_methods)) {\n"; $code .= " trigger_error(\"Method [$alias] is not mocked\");\n"; - $code .= " return;\n"; + $code .= " \$null = null;\n"; + $code .= " return \$null;\n"; $code .= " }\n"; return $code; } - + /** * Creates source code for chaining to the composited * mock object. @@ -1220,24 +1179,24 @@ */ function _chainMockReturns() { $code = " function setReturnValue(\$method, \$value, \$args = false) {\n"; - $code .= Mock::_bailOutIfNotMocked("\$method"); + $code .= $this->_bailOutIfNotMocked("\$method"); $code .= " \$this->_mock->setReturnValue(\$method, \$value, \$args);\n"; $code .= " }\n"; $code .= " function setReturnValueAt(\$timing, \$method, \$value, \$args = false) {\n"; - $code .= Mock::_bailOutIfNotMocked("\$method"); + $code .= $this->_bailOutIfNotMocked("\$method"); $code .= " \$this->_mock->setReturnValueAt(\$timing, \$method, \$value, \$args);\n"; $code .= " }\n"; $code .= " function setReturnReference(\$method, &\$ref, \$args = false) {\n"; - $code .= Mock::_bailOutIfNotMocked("\$method"); + $code .= $this->_bailOutIfNotMocked("\$method"); $code .= " \$this->_mock->setReturnReference(\$method, \$ref, \$args);\n"; $code .= " }\n"; $code .= " function setReturnReferenceAt(\$timing, \$method, &\$ref, \$args = false) {\n"; - $code .= Mock::_bailOutIfNotMocked("\$method"); + $code .= $this->_bailOutIfNotMocked("\$method"); $code .= " \$this->_mock->setReturnReferenceAt(\$timing, \$method, \$ref, \$args);\n"; $code .= " }\n"; return $code; } - + /** * Creates source code for chaining to an aggregated * mock object. @@ -1245,36 +1204,44 @@ * @access private */ function _chainMockExpectations() { - $code = " function expectArguments(\$method, \$args = false) {\n"; - $code .= Mock::_bailOutIfNotMocked("\$method"); + $code = " function expect(\$method, \$args = false) {\n"; + $code .= $this->_bailOutIfNotMocked("\$method"); + $code .= " \$this->_mock->expect(\$method, \$args);\n"; + $code .= " }\n"; + $code .= " function expectArguments(\$method, \$args = false) {\n"; + $code .= $this->_bailOutIfNotMocked("\$method"); $code .= " \$this->_mock->expectArguments(\$method, \$args);\n"; $code .= " }\n"; + $code .= " function expectAt(\$timing, \$method, \$args = false) {\n"; + $code .= $this->_bailOutIfNotMocked("\$method"); + $code .= " \$this->_mock->expectArgumentsAt(\$timing, \$method, \$args);\n"; + $code .= " }\n"; $code .= " function expectArgumentsAt(\$timing, \$method, \$args = false) {\n"; - $code .= Mock::_bailOutIfNotMocked("\$method"); + $code .= $this->_bailOutIfNotMocked("\$method"); $code .= " \$this->_mock->expectArgumentsAt(\$timing, \$method, \$args);\n"; $code .= " }\n"; $code .= " function expectCallCount(\$method, \$count) {\n"; - $code .= Mock::_bailOutIfNotMocked("\$method"); + $code .= $this->_bailOutIfNotMocked("\$method"); $code .= " \$this->_mock->expectCallCount(\$method, \$count);\n"; $code .= " }\n"; $code .= " function expectMaximumCallCount(\$method, \$count) {\n"; - $code .= Mock::_bailOutIfNotMocked("\$method"); + $code .= $this->_bailOutIfNotMocked("\$method"); $code .= " \$this->_mock->expectMaximumCallCount(\$method, \$count);\n"; $code .= " }\n"; $code .= " function expectMinimumCallCount(\$method, \$count) {\n"; - $code .= Mock::_bailOutIfNotMocked("\$method"); + $code .= $this->_bailOutIfNotMocked("\$method"); $code .= " \$this->_mock->expectMinimumCallCount(\$method, \$count);\n"; $code .= " }\n"; $code .= " function expectNever(\$method) {\n"; - $code .= Mock::_bailOutIfNotMocked("\$method"); + $code .= $this->_bailOutIfNotMocked("\$method"); $code .= " \$this->_mock->expectNever(\$method);\n"; $code .= " }\n"; $code .= " function expectOnce(\$method, \$args = false) {\n"; - $code .= Mock::_bailOutIfNotMocked("\$method"); + $code .= $this->_bailOutIfNotMocked("\$method"); $code .= " \$this->_mock->expectOnce(\$method, \$args);\n"; $code .= " }\n"; $code .= " function expectAtLeastOnce(\$method, \$args = false) {\n"; - $code .= Mock::_bailOutIfNotMocked("\$method"); + $code .= $this->_bailOutIfNotMocked("\$method"); $code .= " \$this->_mock->expectAtLeastOnce(\$method, \$args);\n"; $code .= " }\n"; $code .= " function tally() {\n"; @@ -1282,7 +1249,7 @@ $code .= " }\n"; return $code; } - + /** * Creates source code to override a list of methods * with mock versions. @@ -1294,30 +1261,13 @@ function _overrideMethods($methods) { $code = ""; foreach ($methods as $method) { - $code .= " function $method() {\n"; + $code .= " " . $this->_reflection->getSignature($method) . " {\n"; $code .= " \$args = func_get_args();\n"; - $code .= " return \$this->_mock->_invoke(\"$method\", \$args);\n"; + $code .= " \$result = &\$this->_mock->_invoke(\"$method\", \$args);\n"; + $code .= " return \$result;\n"; $code .= " }\n"; } return $code; } - - /** - * Uses a stack trace to find the line of an assertion. - * @param string $format String formatting. - * @param array $stack Stack frames top most first. Only - * needed if not using the PHP - * backtrace function. - * @return string Line number of first expect* - * method embedded in format string. - * @access public - * @static - */ - function getExpectationLine($format = '%d', $stack = false) { - if ($stack === false) { - $stack = SimpleTestCompatibility::getStackTrace(); - } - return SimpleDumper::getFormattedAssertionLine($stack, $format, 'expect'); - } } ?> \ No newline at end of file -- cgit v1.2.3