Completed
Push — master ( af4b60...a13948 )
by Jefersson
11s
created

testWillExecuteLogicInAVoidMethod()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 20
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 11
nc 1
nop 0
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license.
17
 */
18
19
declare(strict_types=1);
20
21
namespace ProxyManagerTest\Functional;
22
23
use PHPUnit_Framework_TestCase;
24
use ProxyManager\Factory\RemoteObject\Adapter\JsonRpc as JsonRpcAdapter;
25
use ProxyManager\Factory\RemoteObject\Adapter\XmlRpc as XmlRpcAdapter;
26
use ProxyManager\Factory\RemoteObject\AdapterInterface;
27
use ProxyManager\Generator\ClassGenerator;
28
use ProxyManager\Generator\Util\UniqueIdentifierGenerator;
29
use ProxyManager\GeneratorStrategy\EvaluatingGeneratorStrategy;
30
use ProxyManager\Proxy\RemoteObjectInterface;
31
use ProxyManager\ProxyGenerator\RemoteObjectGenerator;
32
use ProxyManagerTestAsset\ClassWithSelfHint;
33
use ProxyManagerTestAsset\OtherObjectAccessClass;
34
use ProxyManagerTestAsset\RemoteProxy\Foo;
35
use ProxyManagerTestAsset\RemoteProxy\FooServiceInterface;
36
use ProxyManagerTestAsset\VoidCounter;
37
use ReflectionClass;
38
use Zend\Server\Client;
39
40
/**
41
 * Tests for {@see \ProxyManager\ProxyGenerator\RemoteObjectGenerator} produced objects
42
 *
43
 * @author Vincent Blanchon <[email protected]>
44
 * @license MIT
45
 *
46
 * @group Functional
47
 * @coversNothing
48
 */
49
class RemoteObjectFunctionalTest extends PHPUnit_Framework_TestCase
50
{
51
    /**
52
     * @param mixed  $expectedValue
53
     * @param string $method
54
     * @param array  $params
55
     *
56
     * @return XmlRpcAdapter
57
     */
58
    protected function getXmlRpcAdapter($expectedValue, string $method, array $params) : XmlRpcAdapter
59
    {
60
        /* @var $client Client|\PHPUnit_Framework_MockObject_MockObject */
61
        $client = $this->getMockBuilder(Client::class)->setMethods(['call'])->getMock();
62
63
        $client
64
            ->expects(self::any())
65
            ->method('call')
66
            ->with(self::stringEndsWith($method), $params)
67
            ->will(self::returnValue($expectedValue));
68
69
        $adapter = new XmlRpcAdapter(
70
            $client,
71
            [
72
                 'ProxyManagerTestAsset\RemoteProxy\Foo.foo'
73
                     => 'ProxyManagerTestAsset\RemoteProxy\FooServiceInterface.foo'
74
            ]
75
        );
76
77
        return $adapter;
78
    }
79
80
    /**
81
     * @param mixed  $expectedValue
82
     * @param string $method
83
     * @param array  $params
84
     *
85
     * @return JsonRpcAdapter
86
     */
87
    protected function getJsonRpcAdapter($expectedValue, string $method, array $params) : JsonRpcAdapter
88
    {
89
        /* @var $client Client|\PHPUnit_Framework_MockObject_MockObject */
90
        $client = $this->getMockBuilder(Client::class)->setMethods(['call'])->getMock();
91
92
        $client
93
            ->expects(self::any())
94
            ->method('call')
95
            ->with(self::stringEndsWith($method), $params)
96
            ->will(self::returnValue($expectedValue));
97
98
        $adapter = new JsonRpcAdapter(
99
            $client,
100
            [
101
                 'ProxyManagerTestAsset\RemoteProxy\Foo.foo'
102
                    => 'ProxyManagerTestAsset\RemoteProxy\FooServiceInterface.foo'
103
            ]
104
        );
105
106
        return $adapter;
107
    }
108
109
    /**
110
     * @dataProvider getProxyMethods
111
     *
112
     * @param string|object $instanceOrClassName
113
     * @param string        $method
114
     * @param mixed[]       $params
115
     * @param mixed         $expectedValue
116
     */
117
    public function testXmlRpcMethodCalls($instanceOrClassName, string $method, array $params, $expectedValue) : void
118
    {
119
        $proxyName = $this->generateProxy($instanceOrClassName);
120
121
        /* @var $proxy \ProxyManager\Proxy\RemoteObjectInterface */
122
        $proxy     = $proxyName::staticProxyConstructor($this->getXmlRpcAdapter($expectedValue, $method, $params));
123
124
        self::assertSame($expectedValue, call_user_func_array([$proxy, $method], $params));
125
    }
126
127
    /**
128
     * @dataProvider getProxyMethods
129
     *
130
     * @param string|object $instanceOrClassName
131
     * @param string        $method
132
     * @param mixed[]       $params
133
     * @param mixed         $expectedValue
134
     */
135
    public function testJsonRpcMethodCalls($instanceOrClassName, string $method, array $params, $expectedValue) : void
136
    {
137
        $proxyName = $this->generateProxy($instanceOrClassName);
138
139
        /* @var $proxy \ProxyManager\Proxy\RemoteObjectInterface */
140
        $proxy     = $proxyName::staticProxyConstructor($this->getJsonRpcAdapter($expectedValue, $method, $params));
141
142
        self::assertSame($expectedValue, call_user_func_array([$proxy, $method], $params));
143
    }
144
145
    /**
146
     * @dataProvider getPropertyAccessProxies
147
     *
148
     * @param string|object $instanceOrClassName
149
     * @param string        $publicProperty
150
     * @param string        $propertyValue
151
     */
152
    public function testJsonRpcPropertyReadAccess($instanceOrClassName, string $publicProperty, $propertyValue) : void
153
    {
154
        $proxyName = $this->generateProxy($instanceOrClassName);
155
156
        /* @var $proxy \ProxyManager\Proxy\RemoteObjectInterface */
157
        $proxy     = $proxyName::staticProxyConstructor(
158
            $this->getJsonRpcAdapter($propertyValue, '__get', [$publicProperty])
159
        );
160
161
        /* @var $proxy \ProxyManager\Proxy\NullObjectInterface */
162
        self::assertSame($propertyValue, $proxy->$publicProperty);
163
    }
164
165
    /**
166
     * Generates a proxy for the given class name, and retrieves its class name
167
     *
168
     * @param string|object $parentClassName
169
     *
170
     * @return string
171
     */
172
    private function generateProxy($parentClassName) : string
173
    {
174
        $generatedClassName = __NAMESPACE__ . '\\' . UniqueIdentifierGenerator::getIdentifier('Foo');
175
        $generator          = new RemoteObjectGenerator();
176
        $generatedClass     = new ClassGenerator($generatedClassName);
177
        $strategy           = new EvaluatingGeneratorStrategy();
178
179
        $generator->generate(new ReflectionClass($parentClassName), $generatedClass);
180
        $strategy->generate($generatedClass);
181
182
        return $generatedClassName;
183
    }
184
185
    /**
186
     * Generates a list of object | invoked method | parameters | expected result
187
     *
188
     * @return array
189
     */
190
    public function getProxyMethods() : array
191
    {
192
        $selfHintParam = new ClassWithSelfHint();
193
194
        return [
195
            [
196
                'ProxyManagerTestAsset\RemoteProxy\FooServiceInterface',
197
                'foo',
198
                [],
199
                'bar remote'
200
            ],
201
            [
202
                'ProxyManagerTestAsset\RemoteProxy\Foo',
203
                'foo',
204
                [],
205
                'bar remote'
206
            ],
207
            [
208
                new Foo(),
209
                'foo',
210
                [],
211
                'bar remote'
212
            ],
213
            [
214
                'ProxyManagerTestAsset\RemoteProxy\BazServiceInterface',
215
                'baz',
216
                ['baz'],
217
                'baz remote'
218
            ],
219
            [
220
                new ClassWithSelfHint(),
221
                'selfHintMethod',
222
                [$selfHintParam],
223
                $selfHintParam
224
            ],
225
        ];
226
    }
227
228
    /**
229
     * Generates proxies and instances with a public property to feed to the property accessor methods
230
     *
231
     * @return array
232
     */
233
    public function getPropertyAccessProxies() : array
234
    {
235
        return [
236
            [
237
                FooServiceInterface::class,
238
                'publicProperty',
239
                'publicProperty remote',
240
            ],
241
        ];
242
    }
243
244
    /**
245
     * @group 276
246
     *
247
     * @dataProvider getMethodsThatAccessPropertiesOnOtherObjectsInTheSameScope
248
     *
249
     * @param object $callerObject
250
     * @param object $realInstance
251
     * @param string $method
252
     * @param string $expectedValue
253
     * @param string $propertyName
254
     */
255
    public function testWillInterceptAccessToPropertiesViaFriendClassAccess(
256
        $callerObject,
257
        $realInstance,
258
        string $method,
259
        string $expectedValue,
260
        string $propertyName
261
    ) : void {
262
        $proxyName = $this->generateProxy(get_class($realInstance));
263
264
        /* @var $adapter AdapterInterface|\PHPUnit_Framework_MockObject_MockObject */
265
        $adapter = $this->createMock(AdapterInterface::class);
266
267
        $adapter
268
            ->expects(self::once())
269
            ->method('call')
270
            ->with(get_class($realInstance), '__get', [$propertyName])
271
            ->willReturn($expectedValue);
272
273
        /* @var $proxy OtherObjectAccessClass|RemoteObjectInterface */
274
        $proxy = $proxyName::staticProxyConstructor($adapter);
275
276
        /* @var $accessor callable */
277
        $accessor = [$callerObject, $method];
278
279
        self::assertSame($expectedValue, $accessor($proxy));
280
    }
281
282
    /**
283
     * @group 276
284
     *
285
     * @dataProvider getMethodsThatAccessPropertiesOnOtherObjectsInTheSameScope
286
     *
287
     * @param object $callerObject
288
     * @param object $realInstance
289
     * @param string $method
290
     * @param string $expectedValue
291
     * @param string $propertyName
292
     */
293
    public function testWillInterceptAccessToPropertiesViaFriendClassAccessEvenIfCloned(
294
        $callerObject,
295
        $realInstance,
296
        string $method,
297
        string $expectedValue,
298
        string $propertyName
299
    ) : void {
300
        $proxyName = $this->generateProxy(get_class($realInstance));
301
302
        /* @var $adapter AdapterInterface|\PHPUnit_Framework_MockObject_MockObject */
303
        $adapter = $this->createMock(AdapterInterface::class);
304
305
        $adapter
306
            ->expects(self::once())
307
            ->method('call')
308
            ->with(get_class($realInstance), '__get', [$propertyName])
309
            ->willReturn($expectedValue);
310
311
        /* @var $proxy OtherObjectAccessClass|RemoteObjectInterface */
312
        $proxy = clone $proxyName::staticProxyConstructor($adapter);
313
314
        /* @var $accessor callable */
315
        $accessor = [$callerObject, $method];
316
317
        self::assertSame($expectedValue, $accessor($proxy));
318
    }
319
320
    /**
321
     * @group 327
322
     */
323
    public function testWillExecuteLogicInAVoidMethod() : void
324
    {
325
        $proxyName = $this->generateProxy(VoidCounter::class);
326
327
        /* @var $adapter AdapterInterface|\PHPUnit_Framework_MockObject_MockObject */
328
        $adapter = $this->createMock(AdapterInterface::class);
329
330
        $increment = random_int(10, 1000);
331
332
        $adapter
333
            ->expects(self::once())
334
            ->method('call')
335
            ->with(VoidCounter::class, 'increment', [$increment])
336
            ->willReturn(random_int(10, 1000));
337
338
        /* @var $proxy VoidCounter */
339
        $proxy = clone $proxyName::staticProxyConstructor($adapter);
340
341
        $proxy->increment($increment);
342
    }
343
344
    public function getMethodsThatAccessPropertiesOnOtherObjectsInTheSameScope() : \Generator
345
    {
346
        foreach ((new \ReflectionClass(OtherObjectAccessClass::class))->getProperties() as $property) {
347
            $property->setAccessible(true);
348
349
            $propertyName  = $property->getName();
350
            $realInstance  = new OtherObjectAccessClass();
351
            $expectedValue = uniqid('', true);
352
353
            $property->setValue($realInstance, $expectedValue);
354
355
            yield OtherObjectAccessClass::class . '#$' . $propertyName => [
356
                new OtherObjectAccessClass(),
357
                $realInstance,
358
                'get' . ucfirst($propertyName),
359
                $expectedValue,
360
                $propertyName,
361
            ];
362
        }
363
    }
364
}
365