Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'dev' of github.com:ServiceRunner/lithium into dev

* 'dev' of github.com:ServiceRunner/lithium:
  Doing a recursive unset of _id fields in the DB exporter, as MongoDB doesn't let you use $set and modify _id
  Revert "Modifying Collection and Entity so that you can pass in $options if you so desire. This allows you to"
  Modifying Collection and Entity so that you can pass in $options if you so desire. This allows you to for example pass in custom data handlers if desired for specific object handling when calling Entity->data()
  Revert "Modifying Collection and Entity so that you can pass in $options if you so desire. This allows you to"
  Modifying Collection and Entity so that you can pass in $options if you so desire. This allows you to for example pass in custom data handlers if desired for specific object handling when calling Entity->data()
  Revert "Modifying Collection and Entity so that you can pass in $options if you so desire. This allows you to"
  Modifying Collection and Entity so that you can pass in $options if you so desire. This allows you to for example pass in custom data handlers if desired for specific object handling when calling Entity->data()
  Making Unit return the value of the asserted Expresion consistently
  Revert "Modifying Collection and Entity so that you can pass in $options if you so desire. This allows you to"
  Making Unit return the value of the asserted Expresion consistently
  Fix bug with how results are rendered.
  Delete README.md and leave readme.md - Fixes #759
  Modifying Collection and Entity so that you can pass in $options if you so desire. This allows you to for example pass in custom data handlers if desired for specific object handling when calling Entity->data()
  Add documentation for the mocker class
  • Loading branch information...
commit 7da1ae6eb3f3b4eb13b82bcb9a0f2c3284f316a1 2 parents b1f1030 + 185956e
@leek leek authored
View
15 data/source/mongo_db/Exporter.php
@@ -46,7 +46,20 @@ public static function toCommand($changes) {
}
$result[$to] = $changes[$from];
}
- unset($result['$set']['_id']);
+
+ $unsetKeyRecursive = function(&$array, $key = "_id") use (&$unsetKeyRecursive)
+ {
+ unset($array[$key]);
+
+ foreach($array as &$value) {
+ if(is_array($value)) {
+ $unsetKeyRecursive($value, $key);
+ }
+ }
+ };
+
+ $unsetKeyRecursive($result['$set']);
+
return $result;
}
View
213 test/Mocker.php
@@ -14,39 +14,167 @@
use Reflection;
/**
- * The Mocker class aids in the creation of Mocks on the fly.
+ * The Mocker class aids in the creation of Mocks on the fly, allowing you to
+ * use Lithium filters on most methods in the class.
+ *
+ * To enable the autoloading of mocks you simply need to make a simple method
+ * call.
+ * {{{
+ * use lithium\core\Environment;
+ * use lithium\test\Mocker;
+ * if(!Environment::is('production')) {
+ * Mocker::register();
+ * }
+ * }}}
+ *
+ * You can also enable autoloading inside the setup of a unit test class. This
+ * method can be called redundantly.
+ * {{{
+ * use lithium\test\Mocker;
+ * class MockerTest extends \lithium\test\Unit {
+ * public function setUp() {
+ * Mocker::register();
+ * }
+ * }
+ * }}}
+ *
+ * Using Mocker is the fun magical part, it's autoloaded so simply call the
+ * class you want to mock with the '\Mock' at the end. The autoloader will
+ * detect you want to autoload it, and create it for you. Now you can filter
+ * any method.
+ * {{{
+ * use lithium\console\dispatcher\Mock as DispatcherMock;
+ * $dispatcher = new DispatcherMock();
+ * $dispatcher->applyFilter('config', function($self, $params, $chain) {
+ * return array();
+ * });
+ * $results = $dispatcher->config();
+ * }}}
+ * {{{
+ * use lithium\analysis\parser\Mock as ParserMock;
+ * $code = 'echo "foobar";';
+ * ParserMock::applyFilter('config', function($self, $params, $chain) {
+ * return array();
+ * });
+ * $tokens = ParserMock::tokenize($code, array('wrap' => true));
+ * }}}
*/
class Mocker {
/**
- * A list of code to be generated based on the type.
+ * A list of code to be generated for the delegator.
+ *
+ * The MockDelgate directly extends the mocker and makes all methods
+ * publically available to other classes but should not be accessed directly
+ * by any other application. This should be called only by the mocker and
+ * the mockee and never by the consumer.
*
* @var array
*/
- protected static $_dynamicCode = array(
+ protected static $_mockDelegateIngredients = array(
'startClass' => array(
'namespace {:namespace};',
- 'class {:mockee} extends \{:mocker} {'
+ 'class MockDelegate extends \{:mocker} {'
+ ),
+ 'constructor' => array(
+ '{:modifiers} function __construct({:args}) {',
+ ' $args = func_get_args();',
+ ' $this->parent = array_pop($args);',
+ ' $this->parent->mocker = $this;',
+ ' call_user_func_array("parent::__construct", $args);',
+ '}',
+ ),
+ 'method' => array(
+ '{:modifiers} function {:method}({:args}) {',
+ ' $args = func_get_args();',
+ ' $token = spl_object_hash($this);',
+ ' $id = count($args) - 1;',
+ ' if (!isset($args[$id]) || $args[$id] !== $token) {',
+ ' $method = array($this->parent, "{:method}");',
+ ' return call_user_func_array($method, $args);',
+ ' }',
+ ' array_pop($args);',
+ ' return call_user_func_array("parent::{:method}", $args);',
+ '}',
+ ),
+ 'staticMethod' => array(
+ '{:modifiers} function {:method}({:args}) {',
+ ' $args = func_get_args();',
+ ' $token = "1f3870be274f6c49b3e31a0c6728957f";',
+ ' $id = count($args) - 1;',
+ ' if (!isset($args[$id]) || $args[$id] !== $token) {',
+ ' $method = \'{:namespace}\Mock::{:method}\';',
+ ' return call_user_func_array($method, $args);',
+ ' }',
+ ' array_pop($args);',
+ ' return call_user_func_array("parent::{:method}", $args);',
+ '}',
+ ),
+ 'endClass' => array(
+ '}',
+ ),
+ );
+
+ /**
+ * A list of code to be generated for the mocker.
+ *
+ * The Mock class directly extends the mock class but only directly
+ * interacts with the MockDelegate directly. This class is the actual
+ * interface for consumers, instantiation or static method calls, and can
+ * have most of its methods filtered.
+ *
+ * @var array
+ */
+ protected static $_mockIngredients = array(
+ 'startClass' => array(
+ 'namespace {:namespace};',
+ 'class Mock extends \{:mocker} {',
+ ' public $mocker;',
+ ' protected $_safeVars = array(',
+ ' "_classes",',
+ ' "_methodFilters",',
+ ' "mocker",',
+ ' "_safeVars"',
+ ' );',
+ ),
+ 'get' => array(
+ 'public function __get($key) {',
+ ' return $this->mocker->$key;',
+ '}',
+ ),
+ 'constructor' => array(
+ '{:modifiers} function __construct({:args}) {',
+ ' $args = array_values(get_defined_vars());',
+ ' array_push($args, $this);',
+ ' foreach ($this as $key => $value) {',
+ ' if (!in_array($key, $this->_safeVars)) {',
+ ' unset($this->$key);',
+ ' }',
+ ' }',
+ ' $class = new \ReflectionClass(\'{:namespace}\MockDelegate\');',
+ ' $class->newInstanceArgs($args);',
+ '}',
+ ),
+ 'destructor' => array(
+ 'public function __destruct() {}',
),
'staticMethod' => array(
- '{:modifiers} function {:name}({:params}) {',
- ' $params = func_get_args();',
- ' list($class, $method) = explode(\'::\', __METHOD__, 2);',
- ' $parent = \'parent::\' . $method;',
- ' $result = call_user_func_array($parent, $params);',
- ' return self::_filter($method, $params, function($self, $params) use(&$result) {',
- ' return $result;',
+ '{:modifiers} function {:method}({:args}) {',
+ ' $args = func_get_args();',
+ ' array_push($args, "1f3870be274f6c49b3e31a0c6728957f");',
+ ' $method = \'{:namespace}\MockDelegate::{:method}\';',
+ ' return self::_filter("{:method}", $args, function($self, $args) use(&$method) {',
+ ' return call_user_func_array($method, $args);',
' });',
'}',
),
'method' => array(
- '{:modifiers} function {:name}({:params}) {',
- ' $params = func_get_args();',
- ' list($class, $method) = explode(\'::\', __METHOD__, 2);',
- ' $parent = \'parent::\' . $method;',
- ' $result = call_user_func_array($parent, $params);',
- ' return $this->_filter($parent, $params, function($self, $params) use(&$result) {',
- ' return $result;',
+ '{:modifiers} function {:method}({:args}) {',
+ ' $args = func_get_args();',
+ ' array_push($args, spl_object_hash($this->mocker));',
+ ' $method = array($this->mocker, "{:method}");',
+ ' return $this->_filter(__METHOD__, $args, function($self, $args) use(&$method) {',
+ ' return call_user_func_array($method, $args);',
' });',
'}',
),
@@ -61,12 +189,11 @@ class Mocker {
* @var array
*/
protected static $_blackList = array(
- '__construct', '__destruct', '__call', '__callStatic',
+ '__destruct', '__call', '__callStatic', '_parents',
'__get', '__set', '__isset', '__unset', '__sleep',
'__wakeup', '__toString', '__clone', '__invoke',
- '__construct', '_init', 'applyFilter', 'invokeMethod',
- '__set_state', '_instance', '_filter', '_parents',
- '_stop',
+ '_stop', '_init', 'applyFilter', 'invokeMethod',
+ '__set_state', '_instance', '_filter',
);
/**
@@ -91,29 +218,38 @@ public static function create($mockee) {
$mocker = self::_mocker($mockee);
- $code = self::_dynamicCode('startClass', array(
+ $tokens = array(
'namespace' => self::_namespace($mockee),
'mocker' => $mocker,
- 'mockee' => 'Mock',
- ));
+ 'mockee' => 'MockDelegate',
+ );
+ $mockDelegate = self::_dynamicCode('mockDelegate', 'startClass', $tokens);
+ $mock = self::_dynamicCode('mock', 'startClass', $tokens);
$reflectedClass = new ReflectionClass($mocker);
$reflecedMethods = $reflectedClass->getMethods();
foreach ($reflecedMethods as $method) {
if (!in_array($method->name, self::$_blackList)) {
$key = $method->isStatic() ? 'staticMethod' : 'method';
- $code .= self::_dynamicCode($key, array(
- 'name' => $method->name,
+ $key = $method->name === '__construct' ? 'constructor' : $key;
+ $tokens = array(
+ 'namespace' => self::_namespace($mockee),
+ 'method' => $method->name,
'modifiers' => self::_methodModifiers($method),
- 'params' => self::_methodParams($method),
- 'visibility' => 'public',
- ));
+ 'args' => self::_methodParams($method),
+ 'mocker' => $mocker,
+ );
+ $mockDelegate .= self::_dynamicCode('mockDelegate', $key, $tokens);
+ $mock .= self::_dynamicCode('mock', $key, $tokens);
}
}
- $code .= self::_dynamicCode('endClass');
+ $mockDelegate .= self::_dynamicCode('mockDelegate', 'endClass');
+ $mock .= self::_dynamicCode('mock', 'get');
+ $mock .= self::_dynamicCode('mock', 'destructor');
+ $mock .= self::_dynamicCode('mock', 'endClass');
- eval($code);
+ eval($mockDelegate . $mock);
}
/**
@@ -127,7 +263,8 @@ public static function create($mockee) {
protected static function _methodModifiers(ReflectionMethod $method) {
$modifierKey = $method->getModifiers();
$modifierArray = Reflection::getModifierNames($modifierKey);
- return implode(' ', $modifierArray);
+ $modifiers = implode(' ', $modifierArray);
+ return str_replace(array('private', 'protected'), 'public', $modifiers);
}
/**
@@ -152,12 +289,16 @@ protected static function _methodParams(ReflectionMethod $method) {
/**
* Will generate the code you are wanting.
*
- * @param string $key The key from self::$_dynamicCode
+ * This pulls from $_mockDelegateIngredients and $_mockIngredients.
+ *
+ * @param string $type The name of the array of ingredients to use
+ * @param string $key The key from the array of ingredients
* @param array $tokens Tokens, if any, that should be inserted
* @return string
*/
- protected static function _dynamicCode($key, $tokens = array()) {
- $code = implode("\n", self::$_dynamicCode[$key]);
+ protected static function _dynamicCode($type, $key, $tokens = array()) {
+ $name = '_' . $type . 'Ingredients';
+ $code = implode("\n", self::${$name}[$key]);
return String::insert($code, $tokens) . "\n";
}
View
22 test/Unit.php
@@ -270,7 +270,7 @@ protected function _normalizeLineEndings($expected, $result) {
public function assertEqual($expected, $result, $message = false) {
list($expected, $result) = $this->_normalizeLineEndings($expected, $result);
$data = ($expected != $result) ? $this->_compare('equal', $expected, $result) : null;
- $this->assert($expected == $result, $message, $data);
+ return $this->assert($expected == $result, $message, $data);
}
/**
@@ -282,7 +282,7 @@ public function assertEqual($expected, $result, $message = false) {
*/
public function assertNotEqual($expected, $result, $message = false) {
list($expected, $result) = $this->_normalizeLineEndings($expected, $result);
- $this->assert($result != $expected, $message, compact('expected', 'result'));
+ return $this->assert($result != $expected, $message, compact('expected', 'result'));
}
/**
@@ -294,7 +294,7 @@ public function assertNotEqual($expected, $result, $message = false) {
*/
public function assertIdentical($expected, $result, $message = false) {
$data = ($expected !== $result) ? $this->_compare('identical', $expected, $result) : null;
- $this->assert($expected === $result, $message, $data);
+ return $this->assert($expected === $result, $message, $data);
}
/**
@@ -317,7 +317,7 @@ public function assertIdentical($expected, $result, $message = false) {
*/
public function assertTrue($result, $message = '{:message}') {
$expected = true;
- $this->assert(!empty($result), $message, compact('expected', 'result'));
+ return $this->assert(!empty($result), $message, compact('expected', 'result'));
}
/**
@@ -342,7 +342,7 @@ public function assertTrue($result, $message = '{:message}') {
*/
public function assertFalse($result, $message = '{:message}') {
$expected = false;
- $this->assert(empty($result), $message, compact('expected', 'result'));
+ return $this->assert(empty($result), $message, compact('expected', 'result'));
}
/**
@@ -353,7 +353,7 @@ public function assertFalse($result, $message = '{:message}') {
*/
public function assertNull($result, $message = '{:message}') {
$expected = null;
- $this->assert($result === null, $message, compact('expected', 'result'));
+ return $this->assert($result === null, $message, compact('expected', 'result'));
}
/**
@@ -365,7 +365,7 @@ public function assertNull($result, $message = '{:message}') {
*/
public function assertNoPattern($expected, $result, $message = '{:message}') {
list($expected, $result) = $this->_normalizeLineEndings($expected, $result);
- $this->assert(!preg_match($expected, $result), $message, compact('expected', 'result'));
+ return $this->assert(!preg_match($expected, $result), $message, compact('expected', 'result'));
}
/**
@@ -377,7 +377,7 @@ public function assertNoPattern($expected, $result, $message = '{:message}') {
*/
public function assertPattern($expected, $result, $message = '{:message}') {
list($expected, $result) = $this->_normalizeLineEndings($expected, $result);
- $this->assert(!!preg_match($expected, $result), $message, compact('expected', 'result'));
+ return $this->assert(!!preg_match($expected, $result), $message, compact('expected', 'result'));
}
/**
@@ -600,8 +600,7 @@ public function assertCookie($expected, $headers = null) {
$matched = $this->_cookieMatch($expected, $headers);
if (!$matched['match']) {
$message = sprintf('%s - Cookie not found in headers.', $matched['pattern']);
- $this->assert(false, $message, compact('expected', 'result'));
- return false;
+ return $this->assert(false, $message, compact('expected', 'result'));
}
return $this->assert(true, '%s');
}
@@ -625,8 +624,7 @@ public function assertNoCookie($expected, $headers = null) {
$matched = $this->_cookieMatch($expected, $headers);
if ($matched['match']) {
$message = sprintf('%s - Cookie found in headers.', $matched['pattern']);
- $this->assert(false, $message, compact('expected', 'result'));
- return false;
+ return $this->assert(false, $message, compact('expected', 'result'));
}
return $this->assert(true, '%s');
}
View
26 tests/cases/test/MockerTest.php
@@ -113,6 +113,32 @@ public function testFilteringStaticClassCanReturnOriginal() {
$this->assertEqual($filteredResult, $originalResult);
}
+ public function testOriginalMethodNotCalled() {
+ $http = new \lithium\tests\mocks\security\auth\adapter\mockHttp\Mock;
+
+ $this->assertEqual(0, count($http->headers));
+
+ $http->_writeHeader('Content-type: text/html');
+
+ $this->assertEqual(1, count($http->headers));
+
+ $http->applyFilter('_writeHeader', function($self, $params, $chain) {
+ return false;
+ });
+
+ $http->_writeHeader('Content-type: application/pdf');
+
+ $this->assertEqual(1, count($http->headers));
+ }
+
+ public function testFilteringAFilteredMethod() {
+ $adapt = 'lithium\core\adaptable\Mock';
+ $adapt::applyFilter('_initAdapter', function($self, $params, $chain) {
+ return false;
+ });
+ $this->assertFalse($adapt::_initAdapter('foo', array()));
+ }
+
}
?>
Please sign in to comment.
Something went wrong with that request. Please try again.