Completed
Push — master ( 8dd34a...406ca1 )
by Marco
23:46 queued 44s
created

RemoteObjectFunctionalTest::getProxyMethods()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 127

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 127
rs 8
c 0
b 0
f 0
cc 1
nc 1
nop 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace ProxyManagerTest\Functional;
6
7
use Generator;
8
use Laminas\Server\Client;
9
use PHPUnit\Framework\TestCase;
10
use ProxyManager\Factory\RemoteObject\Adapter\JsonRpc as JsonRpcAdapter;
11
use ProxyManager\Factory\RemoteObject\Adapter\XmlRpc as XmlRpcAdapter;
12
use ProxyManager\Factory\RemoteObject\AdapterInterface;
13
use ProxyManager\Factory\RemoteObjectFactory;
14
use ProxyManagerTestAsset\ClassWithSelfHint;
15
use ProxyManagerTestAsset\OtherObjectAccessClass;
16
use ProxyManagerTestAsset\RemoteProxy\BazServiceInterface;
17
use ProxyManagerTestAsset\RemoteProxy\Foo;
18
use ProxyManagerTestAsset\RemoteProxy\FooServiceInterface;
19
use ProxyManagerTestAsset\RemoteProxy\RemoteServiceWithDefaultsAndVariadicArguments;
20
use ProxyManagerTestAsset\RemoteProxy\RemoteServiceWithDefaultsInterface;
21
use ProxyManagerTestAsset\RemoteProxy\VariadicArgumentsServiceInterface;
22
use ProxyManagerTestAsset\VoidCounter;
23
use ReflectionClass;
24
use function get_class;
25
use function random_int;
26
use function ucfirst;
27
use function uniqid;
28
29
/**
30
 * Tests for {@see \ProxyManager\ProxyGenerator\RemoteObjectGenerator} produced objects
31
 *
32
 * @group Functional
33
 * @coversNothing
34
 */
35
final class RemoteObjectFunctionalTest extends TestCase
36
{
37
    /**
38
     * @param mixed   $expectedValue
39
     * @param mixed[] $parametersExpectedByClient
40
     */
41
    protected function getXmlRpcAdapter($expectedValue, string $method, array $parametersExpectedByClient) : XmlRpcAdapter
42
    {
43
        $client = $this->getMockBuilder(Client::class)->getMock();
44
45
        $client
0 ignored issues
show
Bug introduced by
The method method() does not seem to exist on object<PHPUnit\Framework\MockObject\MockObject>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
46
            ->method('call')
47
            ->with(self::stringEndsWith($method), $parametersExpectedByClient)
48
            ->willReturn($expectedValue);
49
50
        return new XmlRpcAdapter(
51
            $client,
52
            ['ProxyManagerTestAsset\RemoteProxy\Foo.foo' => 'ProxyManagerTestAsset\RemoteProxy\FooServiceInterface.foo']
53
        );
54
    }
55
56
    /**
57
     * @param mixed   $expectedValue
58
     * @param mixed[] $params
59
     */
60
    protected function getJsonRpcAdapter($expectedValue, string $method, array $params) : JsonRpcAdapter
61
    {
62
        $client = $this->getMockBuilder(Client::class)->getMock();
63
64
        $client
0 ignored issues
show
Bug introduced by
The method method() does not seem to exist on object<PHPUnit\Framework\MockObject\MockObject>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
65
            ->method('call')
66
            ->with(self::stringEndsWith($method), $params)
67
            ->willReturn($expectedValue);
68
69
        return new JsonRpcAdapter(
70
            $client,
71
            ['ProxyManagerTestAsset\RemoteProxy\Foo.foo' => 'ProxyManagerTestAsset\RemoteProxy\FooServiceInterface.foo']
72
        );
73
    }
74
75
    /**
76
     * @param string|object $instanceOrClassName
77
     * @param array|mixed[] $passedParams
78
     * @param mixed[]       $callParametersExpectedByAdapter
79
     * @param mixed         $expectedValue
80
     *
81
     * @dataProvider getProxyMethods
82
     *
83
     * @psalm-template OriginalClass of object
84
     * @psalm-param class-string<OriginalClass>|OriginalClass $instanceOrClassName
85
     */
86
    public function testXmlRpcMethodCalls(
87
        $instanceOrClassName,
88
        string $method,
89
        array $passedParams,
90
        array $callParametersExpectedByAdapter,
91
        $expectedValue
92
    ) : void {
93
        $proxy = (new RemoteObjectFactory($this->getXmlRpcAdapter($expectedValue, $method, $callParametersExpectedByAdapter)))
94
            ->createProxy($instanceOrClassName);
95
96
        $callback = [$proxy, $method];
97
98
        self::assertIsCallable($callback);
99
        self::assertSame($expectedValue, $callback(...$passedParams));
100
    }
101
102
    /**
103
     * @param string|object $instanceOrClassName
104
     * @param array|mixed[] $passedParams
105
     * @param mixed[]       $parametersForProxy
106
     * @param mixed         $expectedValue
107
     *
108
     * @dataProvider getProxyMethods
109
     *
110
     * @psalm-template OriginalClass of object
111
     * @psalm-param class-string<OriginalClass>|OriginalClass $instanceOrClassName
112
     */
113
    public function testJsonRpcMethodCalls(
114
        $instanceOrClassName,
115
        string $method,
116
        array $passedParams,
117
        array $parametersForProxy,
118
        $expectedValue
119
    ) : void {
120
        $proxy = (new RemoteObjectFactory($this->getJsonRpcAdapter($expectedValue, $method, $parametersForProxy)))
121
            ->createProxy($instanceOrClassName);
122
123
        $callback = [$proxy, $method];
124
125
        self::assertIsCallable($callback);
126
        self::assertSame($expectedValue, $callback(...$passedParams));
127
    }
128
129
    /**
130
     * @param string|object $instanceOrClassName
131
     * @param mixed         $propertyValue
132
     *
133
     * @dataProvider getPropertyAccessProxies
134
     *
135
     * @psalm-template OriginalClass of object
136
     * @psalm-param class-string<OriginalClass>|OriginalClass $instanceOrClassName
137
     */
138
    public function testJsonRpcPropertyReadAccess($instanceOrClassName, string $publicProperty, $propertyValue) : void
139
    {
140
        $proxy = (new RemoteObjectFactory($this->getJsonRpcAdapter($propertyValue, '__get', [$publicProperty])))
141
            ->createProxy($instanceOrClassName);
142
143
        self::assertSame($propertyValue, $proxy->$publicProperty);
144
    }
145
146
    /**
147
     * Generates a list of object | invoked method | parameters | expected result
148
     *
149
     * @return string[][]|bool[][]|object[][]|mixed[][][]
150
     */
151
    public function getProxyMethods() : array
152
    {
153
        $selfHintParam = new ClassWithSelfHint();
154
155
        return [
156
            [
157
                FooServiceInterface::class,
158
                'foo',
159
                [],
160
                [],
161
                'bar remote',
162
            ],
163
            [
164
                Foo::class,
165
                'foo',
166
                [],
167
                [],
168
                'bar remote',
169
            ],
170
            [
171
                new Foo(),
172
                'foo',
173
                [],
174
                [],
175
                'bar remote',
176
            ],
177
            [
178
                BazServiceInterface::class,
179
                'baz',
180
                ['baz'],
181
                ['baz'],
182
                'baz remote',
183
            ],
184
            [
185
                new ClassWithSelfHint(),
186
                'selfHintMethod',
187
                [$selfHintParam],
188
                [$selfHintParam],
189
                $selfHintParam,
190
            ],
191
            [
192
                VariadicArgumentsServiceInterface::class,
193
                'method',
194
                ['aaa', 1, 2, 3, 4, 5],
195
                ['aaa', 1, 2, 3, 4, 5],
196
                true,
197
            ],
198
            [
199
                RemoteServiceWithDefaultsInterface::class,
200
                'optionalNonNullable',
201
                ['aaa'],
202
                ['aaa', 'Optional parameter to be kept during calls'],
203
                200,
204
            ],
205
            [
206
                RemoteServiceWithDefaultsInterface::class,
207
                'optionalNullable',
208
                ['aaa'],
209
                ['aaa', null],
210
                200,
211
            ],
212
            'when passing only the required parameters' => [
213
                RemoteServiceWithDefaultsInterface::class,
214
                'manyRequiredWithManyOptional',
215
                ['aaa', 100],
216
                [
217
                    'aaa',
218
                    100,
219
                    'Optional parameter to be kept during calls',
220
                    100,
221
                    'Yet another optional parameter to be kept during calls',
222
                ],
223
                200,
224
            ],
225
            'when passing required params and one optional params' => [
226
                RemoteServiceWithDefaultsInterface::class,
227
                'manyRequiredWithManyOptional',
228
                ['aaa', 100, 'passed'],
229
                [
230
                    'aaa',
231
                    100,
232
                    'passed',
233
                    100,
234
                    'Yet another optional parameter to be kept during calls',
235
                ],
236
                200,
237
            ],
238
            'when passing required params and some optional params' => [
239
                RemoteServiceWithDefaultsInterface::class,
240
                'manyRequiredWithManyOptional',
241
                ['aaa', 100, 'passed', 90],
242
                [
243
                    'aaa',
244
                    100,
245
                    'passed',
246
                    90,
247
                    'Yet another optional parameter to be kept during calls',
248
                ],
249
                200,
250
            ],
251
            'when passing only required for method with optional and variadic params' => [
252
                RemoteServiceWithDefaultsAndVariadicArguments::class,
253
                'optionalWithVariadic',
254
                ['aaa'],
255
                [
256
                    'aaa',
257
                    'Optional param to be kept on proxy call',
258
                ],
259
                200,
260
            ],
261
            'when passing required, optional and variadic params' => [
262
                RemoteServiceWithDefaultsAndVariadicArguments::class,
263
                'optionalWithVariadic',
264
                ['aaa', 'Optional param to be kept on proxy call', 10, 20, 30, 50, 90],
265
                [
266
                    'aaa',
267
                    'Optional param to be kept on proxy call',
268
                    10,
269
                    20,
270
                    30,
271
                    50,
272
                    90,
273
                ],
274
                200,
275
            ],
276
        ];
277
    }
278
279
    /**
280
     * Generates proxies and instances with a public property to feed to the property accessor methods
281
     *
282
     * @return string[][]
283
     */
284
    public function getPropertyAccessProxies() : array
285
    {
286
        return [
287
            [
288
                FooServiceInterface::class,
289
                'publicProperty',
290
                'publicProperty remote',
291
            ],
292
        ];
293
    }
294
295
    /**
296
     * @group        276
297
     * @dataProvider getMethodsThatAccessPropertiesOnOtherObjectsInTheSameScope
298
     */
299
    public function testWillInterceptAccessToPropertiesViaFriendClassAccess(
300
        object $callerObject,
301
        object $realInstance,
302
        string $method,
303
        string $expectedValue,
304
        string $propertyName
305
    ) : void {
306
        $adapter = $this->createMock(AdapterInterface::class);
307
308
        $adapter
309
            ->expects(self::once())
310
            ->method('call')
311
            ->with(get_class($realInstance), '__get', [$propertyName])
312
            ->willReturn($expectedValue);
313
314
        $proxy = (new RemoteObjectFactory($adapter))
315
            ->createProxy($realInstance);
316
317
        /** @var callable $accessor */
318
        $accessor = [$callerObject, $method];
319
320
        self::assertSame($expectedValue, $accessor($proxy));
321
    }
322
323
    /**
324
     * @group        276
325
     * @dataProvider getMethodsThatAccessPropertiesOnOtherObjectsInTheSameScope
326
     */
327
    public function testWillInterceptAccessToPropertiesViaFriendClassAccessEvenIfCloned(
328
        object $callerObject,
329
        object $realInstance,
330
        string $method,
331
        string $expectedValue,
332
        string $propertyName
333
    ) : void {
334
        $adapter = $this->createMock(AdapterInterface::class);
335
336
        $adapter
337
            ->expects(self::once())
338
            ->method('call')
339
            ->with(get_class($realInstance), '__get', [$propertyName])
340
            ->willReturn($expectedValue);
341
342
        $proxy = clone (new RemoteObjectFactory($adapter))
343
            ->createProxy($realInstance);
344
345
        /** @var callable $accessor */
346
        $accessor = [$callerObject, $method];
347
348
        self::assertSame($expectedValue, $accessor($proxy));
349
    }
350
351
    /**
352
     * @group 327
353
     */
354
    public function testWillExecuteLogicInAVoidMethod() : void
355
    {
356
        $adapter = $this->createMock(AdapterInterface::class);
357
358
        $increment = random_int(10, 1000);
359
360
        $adapter
361
            ->expects(self::once())
362
            ->method('call')
363
            ->with(VoidCounter::class, 'increment', [$increment])
364
            ->willReturn(random_int(10, 1000));
365
366
        $proxy = clone (new RemoteObjectFactory($adapter))
367
            ->createProxy(VoidCounter::class);
368
369
        $proxy->increment($increment);
370
    }
371
372
    public function getMethodsThatAccessPropertiesOnOtherObjectsInTheSameScope() : Generator
373
    {
374
        foreach ((new ReflectionClass(OtherObjectAccessClass::class))->getProperties() as $property) {
375
            $property->setAccessible(true);
376
377
            $propertyName  = $property->getName();
378
            $realInstance  = new OtherObjectAccessClass();
379
            $expectedValue = uniqid('', true);
380
381
            $property->setValue($realInstance, $expectedValue);
382
383
            yield OtherObjectAccessClass::class . '#$' . $propertyName => [
384
                new OtherObjectAccessClass(),
385
                $realInstance,
386
                'get' . ucfirst($propertyName),
387
                $expectedValue,
388
                $propertyName,
389
            ];
390
        }
391
    }
392
}
393