Passed
Pull Request — master (#3)
by
unknown
01:10
created

InjectorTest::testNullableVariadicArgument()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 9
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Injector\Tests;
6
7
use PHPUnit\Framework\TestCase;
8
use Psr\Container\NotFoundExceptionInterface;
9
use Yiisoft\Di\Container;
10
use Yiisoft\Injector\Injector;
11
use Yiisoft\Injector\InvalidParameterException;
12
use Yiisoft\Injector\MissingRequiredArgumentException;
13
use Yiisoft\Injector\Tests\Support\ColorInterface;
14
use Yiisoft\Injector\Tests\Support\EngineInterface;
15
use Yiisoft\Injector\Tests\Support\EngineMarkTwo;
16
use Yiisoft\Injector\Tests\Support\EngineZIL130;
17
use Yiisoft\Injector\Tests\Support\EngineVAZ2101;
18
use Yiisoft\Injector\Tests\Support\LightEngine;
19
20
class InjectorTest extends TestCase
21
{
22
    public function testInvokeClosure(): void
23
    {
24
        $container = new Container([EngineInterface::class => EngineMarkTwo::class]);
25
26
        $getEngineName = static function (EngineInterface $engine) {
27
            return $engine->getName();
28
        };
29
30
        $engineName = (new Injector($container))->invoke($getEngineName);
31
32
        $this->assertSame('Mark Two', $engineName);
33
    }
34
35
    public function testInvokeCallableArray(): void
36
    {
37
        $container = new Container([]);
38
39
        $object = new EngineVAZ2101();
40
41
        $engineName = (new Injector($container))->invoke([$object, 'rust'], ['index' => 5.0]);
42
43
        $this->assertInstanceOf(EngineVAZ2101::class, $engineName);
44
    }
45
46
    public function testInvokeStatic(): void
47
    {
48
        $container = new Container([]);
49
50
        $engineName = (new Injector($container))->invoke([EngineVAZ2101::class, 'isWroomWroom']);
51
52
        $this->assertIsBool($engineName);
53
    }
54
55
    public function testInvokeWithoutArguments(): void
56
    {
57
        $container = new Container([EngineInterface::class => EngineMarkTwo::class]);
58
59
        $true = fn () => true;
60
61
        $result = (new Injector($container))->invoke($true);
62
63
        $this->assertTrue($result);
64
    }
65
66
    public function testWithNullableArgument(): void
67
    {
68
        $container = new Container([EngineInterface::class => EngineMarkTwo::class]);
69
70
        $nullable = fn (?EngineInterface $engine) => $engine;
71
72
        $result = (new Injector($container))->invoke($nullable);
73
74
        $this->assertNotNull($result);
75
    }
76
77
    public function testWithNullableArgumentAndEmptyContainer(): void
78
    {
79
        $container = new Container([]);
80
81
        $nullable = fn (?EngineInterface $engine) => $engine;
82
83
        $result = (new Injector($container))->invoke($nullable);
84
85
        $this->assertNull($result);
86
    }
87
88
    public function testWithNullableScalarArgument(): void
89
    {
90
        $container = new Container([]);
91
92
        $nullableInt = fn (?int $number) => $number;
93
94
        $result = (new Injector($container))->invoke($nullableInt);
95
96
        $this->assertNull($result);
97
    }
98
99
    public function testWithNullableOptionalArgument(): void
100
    {
101
        $container = new Container([]);
102
103
        $nullableInt = fn (?int $number = 6) => $number;
104
105
        $result = (new Injector($container))->invoke($nullableInt);
106
107
        $this->assertSame(6, $result);
108
    }
109
110
    public function testWithNullableOptionalArgumentThatNull(): void
111
    {
112
        $container = new Container([EngineInterface::class => EngineMarkTwo::class]);
113
114
        $callable = fn (EngineInterface $engine = null) => $engine;
115
116
        $result = (new Injector($container))->invoke($callable);
117
118
        $this->assertNotNull($result);
119
    }
120
121
    public function testCustomDependency(): void
122
    {
123
        $container = new Container([EngineInterface::class => EngineMarkTwo::class]);
124
        $needleEngine = new EngineZIL130();
125
126
        $getEngineName = fn (EngineInterface $engine) => $engine->getName();
127
128
        $engineName = (new Injector($container))->invoke($getEngineName, [$needleEngine]);
129
130
        $this->assertSame(EngineZIL130::NAME, $engineName);
131
    }
132
133
    public function testTwoEqualCustomArgumentsWithOneCustom(): void
134
    {
135
        $container = new Container([EngineInterface::class => EngineMarkTwo::class]);
136
137
        $compareEngines = function (EngineInterface $engine1, EngineInterface $engine2) {
138
            return $engine1->getPower() <=> $engine2->getPower();
139
        };
140
        $zilEngine = new EngineZIL130();
141
142
        $engineName = (new Injector($container))->invoke($compareEngines, [$zilEngine]);
143
144
        $this->assertSame(-1, $engineName);
145
    }
146
147
    public function testTwoEqualCustomArgumentsWithOneCustomNamedParameter(): void
148
    {
149
        $container = new Container([EngineInterface::class => EngineMarkTwo::class]);
150
151
        $compareEngines = function (EngineInterface $engine1, EngineInterface $engine2) {
152
            return $engine1->getPower() <=> $engine2->getPower();
153
        };
154
        $zilEngine = new EngineZIL130();
155
156
        $engineName = (new Injector($container))->invoke($compareEngines, ['engine1' => $zilEngine]);
157
158
        $this->assertSame(-1, $engineName);
159
    }
160
161
    public function testTwoEqualCustomArgumentsWithOneCustomNamedParameter2(): void
162
    {
163
        $container = new Container([EngineInterface::class => EngineMarkTwo::class]);
164
165
        $compareEngines = function (EngineInterface $engine1, EngineInterface $engine2) {
166
            return $engine1->getPower() <=> $engine2->getPower();
167
        };
168
        $zilEngine = new EngineZIL130();
169
170
        $engineName = (new Injector($container))->invoke($compareEngines, ['engine2' => $zilEngine]);
171
172
        $this->assertSame(1, $engineName);
173
    }
174
175
    public function testExtendedArgumentsWithOneCustomNamedParameter2(): void
176
    {
177
        $container = new Container(
178
            [
179
                EngineInterface::class => EngineZIL130::class,
180
                LightEngine::class => EngineVAZ2101::class,
181
            ]
182
        );
183
184
        $compareEngines = function (EngineInterface $engine1, LightEngine $engine2) {
185
            return $engine1->getName() . $engine2->getName();
186
        };
187
        $zilEngine = new EngineMarkTwo();
188
189
        $engineName = (new Injector($container))->invoke($compareEngines, [$zilEngine]);
190
191
        $this->assertSame(EngineMarkTwo::NAME . EngineVAZ2101::NAME, $engineName);
192
    }
193
194
    public function testMissingRequiredTypedParameter(): void
195
    {
196
        $container = new Container([EngineInterface::class => EngineMarkTwo::class]);
197
198
        $getEngineName = static function (EngineInterface $engine, string $two) {
0 ignored issues
show
Unused Code introduced by
The parameter $two is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

198
        $getEngineName = static function (EngineInterface $engine, /** @scrutinizer ignore-unused */ string $two) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
199
            return $engine->getName();
200
        };
201
202
        $injector = new Injector($container);
203
204
        $this->expectException(MissingRequiredArgumentException::class);
205
        $injector->invoke($getEngineName);
206
    }
207
208
    public function testMissingRequiredNotTypedParameter(): void
209
    {
210
        $container = new Container([EngineInterface::class => EngineMarkTwo::class]);
211
212
        $getEngineName = static function (EngineInterface $engine, $two) {
0 ignored issues
show
Unused Code introduced by
The parameter $two is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

212
        $getEngineName = static function (EngineInterface $engine, /** @scrutinizer ignore-unused */ $two) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
213
            return $engine->getName();
214
        };
215
        $injector = new Injector($container);
216
217
        $this->expectException(MissingRequiredArgumentException::class);
218
219
        $injector->invoke($getEngineName);
220
    }
221
222
    public function testNotFoundException(): void
223
    {
224
        $container = new Container([EngineInterface::class => EngineMarkTwo::class]);
225
226
        $getEngineName = static function (EngineInterface $engine, ColorInterface $color) {
0 ignored issues
show
Unused Code introduced by
The parameter $color is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

226
        $getEngineName = static function (EngineInterface $engine, /** @scrutinizer ignore-unused */ ColorInterface $color) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
227
            return $engine->getName();
228
        };
229
230
        $injector = new Injector($container);
231
232
        $this->expectException(NotFoundExceptionInterface::class);
233
        $injector->invoke($getEngineName);
234
    }
235
236
    public function testAloneScalarVariadicArgumentAnsNamedParam(): void
237
    {
238
        $container = new Container([]);
239
240
        $callable = fn (...$var) => array_sum($var);
241
242
        $result = (new Injector($container))->invoke($callable, ['var' => [1, 2, 3]]);
243
244
        $this->assertSame(6, $result);
245
    }
246
247
    public function testScalarVariadicArgumentAnsNamedParam(): void
248
    {
249
        $container = new Container([EngineInterface::class => EngineMarkTwo::class]);
250
251
        $callable = fn (EngineInterface $engine, int ...$var) => array_sum($var);
252
253
        $result = (new Injector($container))->invoke($callable, ['var' => [1, 2, 3]]);
254
255
        $this->assertSame(6, $result);
256
    }
257
258
    public function testVariadicArgumentUnnamedParams(): void
259
    {
260
        $container = new Container([\DateTimeInterface::class => new \DateTimeImmutable()]);
261
262
        $callable = fn (\DateTimeInterface $dateTime, EngineInterface ...$engines) => count($engines);
263
264
        $result = (new Injector($container))->invoke(
265
            $callable,
266
            [new EngineZIL130(), new EngineVAZ2101(), new EngineMarkTwo()]
267
        );
268
269
        $this->assertSame(3, $result);
270
    }
271
272
    public function testVariadicArgumentUnnamedParamsWithIncorrectItem(): void
273
    {
274
        $container = new Container([\DateTimeInterface::class => new \DateTimeImmutable()]);
275
276
        $callable = fn (\DateTimeInterface $dateTime, EngineInterface ...$engines) => count($engines);
277
278
        $result = (new Injector($container))->invoke(
279
            $callable,
280
            [new EngineZIL130(), new EngineVAZ2101(), new EngineMarkTwo(), new \stdClass()]
281
        );
282
283
        // stdClass should be ignored
284
        $this->assertSame(3, $result);
285
    }
286
287
    public function testVariadicMixedArgumentWithMixedParams(): void
288
    {
289
        $container = new Container([\DateTimeInterface::class => new \DateTimeImmutable()]);
290
291
        $callable = fn (...$engines) => $engines;
292
293
        $result = (new Injector($container))->invoke(
294
            $callable,
295
            [new EngineZIL130(), new EngineVAZ2101(), new EngineMarkTwo(), new \stdClass()]
296
        );
297
298
        $this->assertSame(4, count($result));
299
    }
300
301
    public function testVariadicStringArgumentWithUnnamedStringsParams(): void
302
    {
303
        $container = new Container([\DateTimeInterface::class => new \DateTimeImmutable()]);
304
305
        $callable = fn (string ...$engines) => $engines;
306
307
        $this->expectException(\Exception::class);
308
309
        $result = (new Injector($container))->invoke($callable, ['str 1', 'str 2', 'str 3']);
0 ignored issues
show
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
310
    }
311
312
    public function testNullableVariadicArgument(): void
313
    {
314
        $container = new Container([]);
315
316
        $callable = fn (?EngineInterface ...$engines) => $engines;
317
318
        $result = (new Injector($container))->invoke($callable, []);
319
320
        $this->assertSame([], $result);
321
    }
322
323
    public function testAppendingUnusedParams(): void
324
    {
325
        $container = new Container([EngineInterface::class => EngineMarkTwo::class]);
326
327
        $callable = fn (?EngineInterface $engine, $id = 'test') => func_num_args();
0 ignored issues
show
Unused Code introduced by
The parameter $id is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

327
        $callable = fn (?EngineInterface $engine, /** @scrutinizer ignore-unused */ $id = 'test') => func_num_args();

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $engine is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

327
        $callable = fn (/** @scrutinizer ignore-unused */ ?EngineInterface $engine, $id = 'test') => func_num_args();

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
328
329
        $result = (new Injector($container))->invoke($callable, [new \DateTimeImmutable(), new \DateTimeImmutable()]);
330
331
        $this->assertSame(4, $result);
332
    }
333
334
    public function testWrongNamedParam(): void
335
    {
336
        $container = new Container([EngineInterface::class => EngineMarkTwo::class]);
337
338
        $callable = fn (EngineInterface $engine) => $engine;
339
340
        $this->expectException(\Throwable::class);
341
342
        $result = (new Injector($container))->invoke($callable, ['engine' => new \DateTimeImmutable()]);
0 ignored issues
show
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
343
    }
344
345
    public function testArrayArgumentWithUnnamedType(): void
346
    {
347
        $container = new Container([EngineInterface::class => EngineMarkTwo::class]);
348
349
        $callable = fn (array $arg) => $arg;
350
351
        $this->expectException(MissingRequiredArgumentException::class);
352
353
        (new Injector($container))->invoke($callable, [['test']]);
354
    }
355
356
    public function testCallableArgumentWithUnnamedType(): void
357
    {
358
        $container = new Container([EngineInterface::class => EngineMarkTwo::class]);
359
360
        $callable = fn (callable $arg) => $arg();
361
362
        $this->expectException(MissingRequiredArgumentException::class);
363
364
        (new Injector($container))->invoke($callable, [fn () => true]);
365
    }
366
367
    public function testIterableArgumentWithUnnamedType(): void
368
    {
369
        $container = new Container([EngineInterface::class => EngineMarkTwo::class]);
370
371
        $callable = fn (iterable $arg) => $arg;
372
373
        $this->expectException(MissingRequiredArgumentException::class);
374
375
        (new Injector($container))->invoke($callable, [new \SplStack()]);
376
    }
377
378
    public function testUnnamedScalarParam(): void
379
    {
380
        $container = new Container([]);
381
382
        $getEngineName = fn () => 42;
383
384
        $this->expectException(InvalidParameterException::class);
385
386
        (new Injector($container))->invoke($getEngineName, ['test']);
387
    }
388
}
389