Skip to content

Commit

Permalink
Merge pull request #358 from asmblah/bugfix/circular-args
Browse files Browse the repository at this point in the history
Fixed `var_export does not handle circular references` issue
  • Loading branch information
davedevelopment committed Jul 28, 2014
2 parents 1b3b265 + b9a77bd commit 5c8d5ee
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 15 deletions.
50 changes: 38 additions & 12 deletions library/Mockery.php
Original file line number Diff line number Diff line change
Expand Up @@ -370,18 +370,7 @@ public static function formatArgs($method, array $args = null)
if ($args && !empty($args)) {
$parts = array();
foreach($args as $arg) {
if (is_object($arg)) {
$parts[] = get_class($arg);
} elseif (is_int($arg) || is_float($arg)) {
$parts[] = $arg;
} elseif (is_array($arg)) {
$arg = preg_replace("{\s}", '', var_export($arg, true));
$parts[] = (strlen($arg) > 1000) ? substr($arg, 0, 1000).'...)' : $arg;
} elseif (is_bool($arg)) {
$parts[] = $arg ? 'true' : 'false';
} else {
$parts[] = '"' . (string) $arg . '"';
}
$parts[] = self::formatArg($arg);
}
$return .= implode(', ', $parts); // TODO: improve format

Expand All @@ -390,6 +379,43 @@ public static function formatArgs($method, array $args = null)
return $return;
}

private static function formatArg($arg, $depth = 0)
{
if (is_object($arg)) {
return 'object(' . get_class($arg) . ')';
}

if (is_int($arg) || is_float($arg)) {
return $arg;
}

if (is_array($arg)) {
if ($depth === 1) {
$arg = 'array(...)';
} else {
$sample = array();
foreach ($arg as $key => $value) {
$sample[$key] = self::formatArg($value, $depth + 1);
}
$arg = preg_replace("{\s}", '', var_export($sample, true));
}

return ((strlen($arg) > 1000) ? substr($arg, 0, 1000).'...)' : $arg);
}

if (is_bool($arg)) {
return $arg ? 'true' : 'false';
}

if (is_resource($arg)) {
return 'resource(...)';
}

$arg = (string) $arg;

return $depth === 0 ? '"' . $arg . '"' : $arg;
}

/**
* Utility function to format objects to printable arrays
*
Expand Down
96 changes: 94 additions & 2 deletions tests/Mockery/ContainerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1133,8 +1133,100 @@ public function testThrowsWhenNamedMockClassExistsAndIsNotMockery()
$mock = $this->container->mock($builder);
}

/**
* @test
/**
* @expectedException Mockery\Exception\NoMatchingExpectationException
* @expectedExceptionMessage MyTestClass::foo(resource(...))
*/
public function testHandlesMethodWithArgumentExpectationWhenCalledWithResource()
{
$mock = $this->container->mock('MyTestClass');
$mock->shouldReceive('foo')->with(array('yourself' => 21));

$mock->foo(fopen('php://memory', 'r'));
}

/**
* @expectedException Mockery\Exception\NoMatchingExpectationException
* @expectedExceptionMessage MyTestClass::foo(array('myself'=>'array(...)',))
*/
public function testHandlesMethodWithArgumentExpectationWhenCalledWithCircularArray()
{
$testArray = array();
$testArray['myself'] =& $testArray;

$mock = $this->container->mock('MyTestClass');
$mock->shouldReceive('foo')->with(array('yourself' => 21));

$mock->foo($testArray);
}

/**
* @expectedException Mockery\Exception\NoMatchingExpectationException
* @expectedExceptionMessage MyTestClass::foo(array('a_scalar'=>2,'an_array'=>'array(...)',))
*/
public function testHandlesMethodWithArgumentExpectationWhenCalledWithNestedArray()
{
$testArray = array();
$testArray['a_scalar'] = 2;
$testArray['an_array'] = array(1, 2, 3);

$mock = $this->container->mock('MyTestClass');
$mock->shouldReceive('foo')->with(array('yourself' => 21));

$mock->foo($testArray);
}

/**
* @expectedException Mockery\Exception\NoMatchingExpectationException
* @expectedExceptionMessage MyTestClass::foo(array('a_scalar'=>2,'an_object'=>'object(stdClass)',))
*/
public function testHandlesMethodWithArgumentExpectationWhenCalledWithNestedObject()
{
$testArray = array();
$testArray['a_scalar'] = 2;
$testArray['an_object'] = new \stdClass();

$mock = $this->container->mock('MyTestClass');
$mock->shouldReceive('foo')->with(array('yourself' => 21));

$mock->foo($testArray);
}

/**
* @expectedException Mockery\Exception\NoMatchingExpectationException
* @expectedExceptionMessage MyTestClass::foo(array('a_scalar'=>2,'a_closure'=>'object(Closure)',))
*/
public function testHandlesMethodWithArgumentExpectationWhenCalledWithNestedClosure()
{
$testArray = array();
$testArray['a_scalar'] = 2;
$testArray['a_closure'] = function () {
};

$mock = $this->container->mock('MyTestClass');
$mock->shouldReceive('foo')->with(array('yourself' => 21));

$mock->foo($testArray);
}

/**
* @expectedException Mockery\Exception\NoMatchingExpectationException
* @expectedExceptionMessage MyTestClass::foo(array('a_scalar'=>2,'a_resource'=>'resource(...)',))
*/
public function testHandlesMethodWithArgumentExpectationWhenCalledWithNestedResource()
{
$testArray = array();
$testArray['a_scalar'] = 2;
$testArray['a_resource'] = fopen('php://memory', 'r');

$mock = $this->container->mock('MyTestClass');
$mock->shouldReceive('foo')->with(array('yourself' => 21));

$mock->foo($testArray);
}

/**
* @test
* @group issue/339
*/
public function canMockClassesThatDescendFromInternalClasses()
Expand Down
2 changes: 1 addition & 1 deletion tests/Mockery/ExpectationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -722,7 +722,7 @@ public function testEnsuresOrderingIsCrossMockWhenGloballyFlagSet()
public function testExpectationCastToStringFormatting()
{
$exp = $this->mock->shouldReceive('foo')->with(1, 'bar', new stdClass, array('Spam' => 'Ham', 'Bar' => 'Baz'));
$this->assertEquals('[foo(1, "bar", stdClass, array(\'Spam\'=>\'Ham\',\'Bar\'=>\'Baz\',))]', (string) $exp);
$this->assertEquals('[foo(1, "bar", object(stdClass), array(\'Spam\'=>\'Ham\',\'Bar\'=>\'Baz\',))]', (string) $exp);
}

public function testLongExpectationCastToStringFormatting()
Expand Down

0 comments on commit 5c8d5ee

Please sign in to comment.