Skip to content

Commit 49f5ae8

Browse files
committed
Adding some test, changing the class to find scope objet after call_user_func and similar things
1 parent 6f4e1af commit 49f5ae8

File tree

3 files changed

+174
-92
lines changed

3 files changed

+174
-92
lines changed

PHPUnit/Extensions/MockFunction.php

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ public function __construct( $function_name, $scope_object )
120120
*
121121
* Makes sure the replaced functions are finally cleared in case runkit
122122
* "forgets" to remove them in the end of the request.
123-
* It is still higgly recommended to call restore() explciitly!
123+
* It is still highly recommended to call restore() explicitly!
124124
*/
125125
public function __destruct()
126126
{
@@ -146,9 +146,9 @@ public function restore()
146146
}
147147

148148
if ( isset( self::$instances[$this->id] ) )
149-
{
150-
unset( self::$instances[$this->id] );
151-
}
149+
{
150+
unset( self::$instances[$this->id] );
151+
}
152152
}
153153

154154
/**
@@ -274,6 +274,9 @@ protected function getCallback()
274274
* In theory we should instement the distance by one because when we call this
275275
* method, we don't count it itself to the callstack, but since the stack is
276276
* 0-indexed, we can avoid this step.
277+
*
278+
* Function calls are ignored, the first call after $distance that is made form
279+
* is returned.
277280
*
278281
* @param type $distance The distance in the call stack from the current call and the desired one.
279282
* @return object
@@ -282,10 +285,19 @@ protected static function getCallStackObject( $distance )
282285
{
283286
$backtrace = debug_backtrace();
284287

285-
if ( isset( $backtrace[$distance]['object'] ) )
286-
{
287-
return $backtrace[$distance]['object'];
288-
}
288+
do
289+
{
290+
if ( isset( $backtrace[$distance]['object'] ) )
291+
{
292+
return $backtrace[$distance]['object'];
293+
}
294+
295+
/* If there is no object assiciated to this call, we go further until
296+
* the next one.
297+
* Funcsion calls and functions like "user_call_func" get ignored.
298+
*/
299+
++$distance;
300+
} while ( isset( $backtrace[$distance] ) );
289301

290302
return null;
291303
}

Tests/Extensions/MockFunctionTest.php

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
<?php
2+
3+
require_once dirname(__FILE__) . '/../../PHPUnit/Extensions/MockFunction.php';
4+
5+
/**
6+
* @covers PHPUnit_Extensions_MockFunction
7+
*/
8+
class Tests_Extensions_MockFunctionTest extends PHPUnit_Framework_TestCase
9+
{
10+
11+
/**
12+
* Object to test.
13+
*
14+
* @var PHPUnit_Extensions_MockFunction
15+
*/
16+
protected $object;
17+
18+
/**
19+
* Scope object to use for calling mocked functions.
20+
*
21+
* @var PHPUnit_Extensions_MockFunction
22+
*/
23+
protected $test_scope_object;
24+
25+
/**
26+
* Name of the mocked function.
27+
*
28+
* @var PHPUnit_Extensions_MockFunction
29+
*/
30+
protected $test_function_name;
31+
32+
/**
33+
* Setting up.
34+
*/
35+
protected function setUp()
36+
{
37+
$this->test_scope_object = new TestScopeObject();
38+
}
39+
40+
protected function onNotSuccessfulTest( Exception $e )
41+
{
42+
var_dump( $e->getMessage() );
43+
var_dump( $e->getTraceAsString() );
44+
}
45+
46+
/**
47+
* Test simple function return faking without consraints.
48+
*/
49+
public function testMockWithReturn()
50+
{
51+
$this->test_function_name = self::getFunctionName( 'time' );
52+
$this->object = new PHPUnit_Extensions_MockFunction( $this->test_function_name, $this->test_scope_object );
53+
54+
// Back to the future:
55+
$einsteins_clock = time() + 60;
56+
$this->object->expects( $this->atLeastOnce() )->will( $this->returnValue( $einsteins_clock ) );
57+
58+
// Einstein's clock is exactly one minute behind mine.
59+
$this->assertSame( $einsteins_clock, $this->test_scope_object->callFunction( $this->test_function_name, array() ) );
60+
61+
$this->object->restore();
62+
63+
// We are back in 1985.
64+
$this->assertSame( time(), $this->test_scope_object->callFunction( $this->test_function_name, array() ) );
65+
}
66+
67+
/**
68+
* Test more advanced mocking with return callback and constraints.
69+
*/
70+
public function testMockWithOriginal()
71+
{
72+
$this->test_function_name = self::getFunctionName( 'strrev' );
73+
$this->object = new PHPUnit_Extensions_MockFunction( $this->test_function_name, $this->test_scope_object );
74+
75+
// Return normally, only checks the call.
76+
$this->object->expects( $this->once() )->with( $this->equalTo( 'abc' ) )->will( $this->returnCallback( 'strrev' ) );
77+
78+
// The same output is returned.
79+
$this->assertSame( 'cba', $this->test_scope_object->callFunction( $this->test_function_name, array( 'abc' ) ) );
80+
81+
$this->object->restore();
82+
}
83+
84+
/**
85+
* Testing newly created function.
86+
*/
87+
public function testMockNewFunction()
88+
{
89+
$this->test_function_name = 'new_random_function_' . uniqid();
90+
$this->object = new PHPUnit_Extensions_MockFunction( $this->test_function_name, $this->test_scope_object );
91+
92+
// Return normally, only checks the call.
93+
$this->object->expects( $this->any() )->will( $this->returnValue( 'OK' ) );
94+
95+
$this->assertSame( 'OK', $this->test_scope_object->callFunction( $this->test_function_name, array() ) );
96+
97+
$this->object->restore();
98+
}
99+
100+
protected static function getFunctionName( $function_name )
101+
{
102+
// Memoization for config value.
103+
static $internal_override_on;
104+
105+
if ( !isset( $internal_override_on ) )
106+
{
107+
$internal_override_on = (bool) ini_get( 'runkit.internal_override' );
108+
}
109+
110+
if ( $internal_override_on )
111+
{
112+
return $function_name;
113+
}
114+
115+
$proxy_function_name = 'proxy_to_' . $function_name . '_' . uniqid();
116+
117+
eval( <<<PROXY
118+
function $proxy_function_name()
119+
{
120+
\$arguments = func_get_args();
121+
return call_user_func_array( '$function_name', \$arguments );
122+
}
123+
PROXY
124+
);
125+
126+
return $proxy_function_name;
127+
}
128+
129+
}
130+
131+
/**
132+
* Class to be used as scope object for mocked function calls.
133+
*/
134+
class TestScopeObject
135+
{
136+
/**
137+
* Simply calls a PHP callback with the passed parameters.
138+
*
139+
* @throws InvalidArgumentException In case $callback is not callable.
140+
* @param callback $callback
141+
* @param array $params
142+
* @return mixed The result of the callback execution.
143+
*/
144+
public function callFunction( $callback, array $params )
145+
{
146+
if ( !is_callable( $callback ) )
147+
{
148+
throw new InvalidArgumentException( 'Invalid callback at parameter 1st' );
149+
}
150+
return call_user_func_array( $callback, $params );
151+
}
152+
}
153+
154+
?>

Tests/Extensions/PHPUnit_Extensions_MockFunctionTest.php

Lines changed: 0 additions & 84 deletions
This file was deleted.

0 commit comments

Comments
 (0)