Completed
Push — master ( 9cf5c1...719e81 )
by Jaap
01:46
created

TypeResolverTest::testResolvingNullableTypes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 6
nc 1
nop 0
1
<?php
2
/**
3
 * This file is part of phpDocumentor.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 *
8
 * @copyright 2010-2015 Mike van Riel<[email protected]>
9
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
10
 * @link      http://phpdoc.org
11
 */
12
13
namespace phpDocumentor\Reflection;
14
15
use Mockery as m;
16
use phpDocumentor\Reflection\Types\Array_;
17
use phpDocumentor\Reflection\Types\Compound;
18
use phpDocumentor\Reflection\Types\Context;
19
use phpDocumentor\Reflection\Types\Iterable_;
20
use phpDocumentor\Reflection\Types\Nullable;
21
use phpDocumentor\Reflection\Types\Object_;
22
use phpDocumentor\Reflection\Types\String_;
23
24
/**
25
 * @coversDefaultClass phpDocumentor\Reflection\TypeResolver
26
 */
27
class TypeResolverTest extends \PHPUnit_Framework_TestCase
28
{
29
    /**
30
     * @param string $keyword
31
     * @param string $expectedClass
32
     *
33
     * @covers ::__construct
34
     * @covers ::resolve
35
     * @covers ::<private>
36
     *
37
     * @uses phpDocumentor\Reflection\Types\Context
38
     * @uses phpDocumentor\Reflection\Types\Array_
39
     * @uses phpDocumentor\Reflection\Types\Object_
40
     *
41
     * @dataProvider provideKeywords
42
     */
43
    public function testResolvingKeywords($keyword, $expectedClass)
44
    {
45
        $fixture = new TypeResolver();
46
47
        $resolvedType = $fixture->resolve($keyword, new Context(''));
48
49
        $this->assertInstanceOf($expectedClass, $resolvedType);
50
    }
51
52
    /**
53
     * @param string $fqsen
54
     *
55
     * @covers ::__construct
56
     * @covers ::resolve
57
     * @covers ::<private>
58
     *
59
     * @uses phpDocumentor\Reflection\Types\Context
60
     * @uses phpDocumentor\Reflection\Types\Object_
61
     * @uses phpDocumentor\Reflection\Fqsen
62
     * @uses phpDocumentor\Reflection\FqsenResolver
63
     *
64
     * @dataProvider provideFqcn
65
     */
66
    public function testResolvingFQSENs($fqsen)
67
    {
68
        $fixture = new TypeResolver();
69
70
        /** @var Object_ $resolvedType */
71
        $resolvedType = $fixture->resolve($fqsen, new Context(''));
72
73
        $this->assertInstanceOf('phpDocumentor\Reflection\Types\Object_', $resolvedType);
74
        $this->assertInstanceOf('phpDocumentor\Reflection\Fqsen', $resolvedType->getFqsen());
75
        $this->assertSame($fqsen, (string)$resolvedType);
76
    }
77
78
    /**
79
     * @covers ::__construct
80
     * @covers ::resolve
81
     * @covers ::<private>
82
     *
83
     * @uses phpDocumentor\Reflection\Types\Context
84
     * @uses phpDocumentor\Reflection\Types\Object_
85
     * @uses phpDocumentor\Reflection\Fqsen
86
     * @uses phpDocumentor\Reflection\FqsenResolver
87
     */
88
    public function testResolvingRelativeQSENsBasedOnNamespace()
89
    {
90
        $fixture = new TypeResolver();
91
92
        /** @var Object_ $resolvedType */
93
        $resolvedType = $fixture->resolve('DocBlock', new Context('phpDocumentor\Reflection'));
94
95
        $this->assertInstanceOf('phpDocumentor\Reflection\Types\Object_', $resolvedType);
96
        $this->assertInstanceOf('phpDocumentor\Reflection\Fqsen', $resolvedType->getFqsen());
97
        $this->assertSame('\phpDocumentor\Reflection\DocBlock', (string)$resolvedType);
98
    }
99
100
    /**
101
     * @covers ::__construct
102
     * @covers ::resolve
103
     * @covers ::<private>
104
     *
105
     * @uses phpDocumentor\Reflection\Types\Context
106
     * @uses phpDocumentor\Reflection\Types\Object_
107
     * @uses phpDocumentor\Reflection\Fqsen
108
     * @uses phpDocumentor\Reflection\FqsenResolver
109
     */
110
    public function testResolvingRelativeQSENsBasedOnNamespaceAlias()
111
    {
112
        $fixture = new TypeResolver();
113
114
        /** @var Object_ $resolvedType */
115
        $resolvedType = $fixture->resolve(
116
            'm\MockInterface',
117
            new Context('phpDocumentor\Reflection', ['m' => '\Mockery'])
118
        );
119
120
        $this->assertInstanceOf('phpDocumentor\Reflection\Types\Object_', $resolvedType);
121
        $this->assertInstanceOf('phpDocumentor\Reflection\Fqsen', $resolvedType->getFqsen());
122
        $this->assertSame('\Mockery\MockInterface', (string)$resolvedType);
123
    }
124
125
    /**
126
     * @covers ::__construct
127
     * @covers ::resolve
128
     * @covers ::<private>
129
     *
130
     * @uses phpDocumentor\Reflection\Types\Context
131
     * @uses phpDocumentor\Reflection\Types\Array_
132
     * @uses phpDocumentor\Reflection\Types\String_
133
     */
134
    public function testResolvingTypedArrays()
135
    {
136
        $fixture = new TypeResolver();
137
138
        /** @var Array_ $resolvedType */
139
        $resolvedType = $fixture->resolve('string[]', new Context(''));
140
141
        $this->assertInstanceOf('phpDocumentor\Reflection\Types\Array_', $resolvedType);
142
        $this->assertSame('string[]', (string)$resolvedType);
143
        $this->assertInstanceOf('phpDocumentor\Reflection\Types\Compound', $resolvedType->getKeyType());
144
        $this->assertInstanceOf('phpDocumentor\Reflection\Types\String_', $resolvedType->getValueType());
145
    }
146
147
    /**
148
     * @covers ::__construct
149
     * @covers ::resolve
150
     * @covers ::<private>
151
     *
152
     * @uses \phpDocumentor\Reflection\Types\Context
153
     * @uses \phpDocumentor\Reflection\Types\Nullable
154
     * @uses \phpDocumentor\Reflection\Types\String_
155
     */
156
    public function testResolvingNullableTypes()
157
    {
158
        $fixture = new TypeResolver();
159
160
        /** @var Nullable $resolvedType */
161
        $resolvedType = $fixture->resolve('?string', new Context(''));
162
163
        $this->assertInstanceOf(Nullable::class, $resolvedType);
164
        $this->assertInstanceOf(String_::class, $resolvedType->getActualType());
165
        $this->assertSame('?string', (string)$resolvedType);
166
    }
167
168
    /**
169
     * @covers ::__construct
170
     * @covers ::resolve
171
     * @covers ::<private>
172
     *
173
     * @uses phpDocumentor\Reflection\Types\Context
174
     * @uses phpDocumentor\Reflection\Types\Array_
175
     * @uses phpDocumentor\Reflection\Types\String_
176
     */
177
    public function testResolvingNestedTypedArrays()
178
    {
179
        $fixture = new TypeResolver();
180
181
        /** @var Array_ $resolvedType */
182
        $resolvedType = $fixture->resolve('string[][]', new Context(''));
183
184
        /** @var Array_ $childValueType */
185
        $childValueType = $resolvedType->getValueType();
186
187
        $this->assertInstanceOf('phpDocumentor\Reflection\Types\Array_', $resolvedType);
188
189
        $this->assertSame('string[][]', (string)$resolvedType);
190
        $this->assertInstanceOf('phpDocumentor\Reflection\Types\Compound', $resolvedType->getKeyType());
191
        $this->assertInstanceOf('phpDocumentor\Reflection\Types\Array_', $childValueType);
192
193
        $this->assertSame('string[]', (string)$childValueType);
194
        $this->assertInstanceOf('phpDocumentor\Reflection\Types\Compound', $childValueType->getKeyType());
195
        $this->assertInstanceOf('phpDocumentor\Reflection\Types\String_', $childValueType->getValueType());
196
    }
197
198
    /**
199
     * @covers ::__construct
200
     * @covers ::resolve
201
     * @covers ::<private>
202
     *
203
     * @uses phpDocumentor\Reflection\Types\Context
204
     * @uses phpDocumentor\Reflection\Types\Compound
205
     * @uses phpDocumentor\Reflection\Types\String_
206
     * @uses phpDocumentor\Reflection\Types\Object_
207
     * @uses phpDocumentor\Reflection\Fqsen
208
     * @uses phpDocumentor\Reflection\FqsenResolver
209
     */
210
    public function testResolvingCompoundTypes()
211
    {
212
        $fixture = new TypeResolver();
213
214
        /** @var Compound $resolvedType */
215
        $resolvedType = $fixture->resolve('string|Reflection\DocBlock', new Context('phpDocumentor'));
216
217
        $this->assertInstanceOf('phpDocumentor\Reflection\Types\Compound', $resolvedType);
218
        $this->assertSame('string|\phpDocumentor\Reflection\DocBlock', (string)$resolvedType);
219
220
        /** @var String $secondType */
221
        $firstType = $resolvedType->get(0);
222
223
        /** @var Object_ $secondType */
224
        $secondType = $resolvedType->get(1);
225
226
        $this->assertInstanceOf('phpDocumentor\Reflection\Types\String_', $firstType);
227
        $this->assertInstanceOf('phpDocumentor\Reflection\Types\Object_', $secondType);
228
        $this->assertInstanceOf('phpDocumentor\Reflection\Fqsen', $secondType->getFqsen());
229
    }
230
231
    /**
232
     * @covers ::__construct
233
     * @covers ::resolve
234
     * @covers ::<private>
235
     *
236
     * @uses phpDocumentor\Reflection\Types\Context
237
     * @uses phpDocumentor\Reflection\Types\Compound
238
     * @uses phpDocumentor\Reflection\Types\Array_
239
     * @uses phpDocumentor\Reflection\Types\Object_
240
     * @uses phpDocumentor\Reflection\Fqsen
241
     * @uses phpDocumentor\Reflection\FqsenResolver
242
     */
243
    public function testResolvingCompoundTypedArrayTypes()
244
    {
245
        $fixture = new TypeResolver();
246
247
        /** @var Compound $resolvedType */
248
        $resolvedType = $fixture->resolve('\stdClass[]|Reflection\DocBlock[]', new Context('phpDocumentor'));
249
250
        $this->assertInstanceOf('phpDocumentor\Reflection\Types\Compound', $resolvedType);
251
        $this->assertSame('\stdClass[]|\phpDocumentor\Reflection\DocBlock[]', (string)$resolvedType);
252
253
        /** @var Array_ $secondType */
254
        $firstType = $resolvedType->get(0);
255
256
        /** @var Array_ $secondType */
257
        $secondType = $resolvedType->get(1);
258
259
        $this->assertInstanceOf('phpDocumentor\Reflection\Types\Array_', $firstType);
260
        $this->assertInstanceOf('phpDocumentor\Reflection\Types\Array_', $secondType);
261
        $this->assertInstanceOf('phpDocumentor\Reflection\Types\Object_', $firstType->getValueType());
262
        $this->assertInstanceOf('phpDocumentor\Reflection\Types\Object_', $secondType->getValueType());
263
    }
264
265
    /**
266
     * This test asserts that the parameter order is correct.
267
     *
268
     * When you pass two arrays separated by the compound operator (i.e. 'integer[]|string[]') then we always split the
269
     * expression in its compound parts and then we parse the types with the array operators. If we were to switch the
270
     * order around then 'integer[]|string[]' would read as an array of string or integer array; which is something
271
     * other than what we intend.
272
     *
273
     * @covers ::__construct
274
     * @covers ::resolve
275
     * @covers ::<private>
276
     *
277
     * @uses phpDocumentor\Reflection\Types\Context
278
     * @uses phpDocumentor\Reflection\Types\Compound
279
     * @uses phpDocumentor\Reflection\Types\Array_
280
     * @uses phpDocumentor\Reflection\Types\Integer
281
     * @uses phpDocumentor\Reflection\Types\String_
282
     */
283
    public function testResolvingCompoundTypesWithTwoArrays()
284
    {
285
        $fixture = new TypeResolver();
286
287
        /** @var Compound $resolvedType */
288
        $resolvedType = $fixture->resolve('integer[]|string[]', new Context(''));
289
290
        $this->assertInstanceOf('phpDocumentor\Reflection\Types\Compound', $resolvedType);
291
        $this->assertSame('int[]|string[]', (string)$resolvedType);
292
293
        /** @var Array_ $firstType */
294
        $firstType = $resolvedType->get(0);
295
296
        /** @var Array_ $secondType */
297
        $secondType = $resolvedType->get(1);
298
299
        $this->assertInstanceOf('phpDocumentor\Reflection\Types\Array_', $firstType);
300
        $this->assertInstanceOf('phpDocumentor\Reflection\Types\Integer', $firstType->getValueType());
301
        $this->assertInstanceOf('phpDocumentor\Reflection\Types\Array_', $secondType);
302
        $this->assertInstanceOf('phpDocumentor\Reflection\Types\String_', $secondType->getValueType());
303
    }
304
305
    /**
306
     * @covers ::__construct
307
     * @covers ::addKeyword
308
     * @uses phpDocumentor\Reflection\TypeResolver::resolve
309
     * @uses phpDocumentor\Reflection\TypeResolver::<private>
310
     * @uses phpDocumentor\Reflection\Types\Context
311
     */
312
    public function testAddingAKeyword()
313
    {
314
        // Assign
315
        $typeMock = m::mock(Type::class);
316
317
        // Act
318
        $fixture = new TypeResolver();
319
        $fixture->addKeyword('mock', get_class($typeMock));
320
321
        // Assert
322
        $result = $fixture->resolve('mock', new Context(''));
323
        $this->assertInstanceOf(get_class($typeMock), $result);
324
        $this->assertNotSame($typeMock, $result);
325
    }
326
327
    /**
328
     * @covers ::__construct
329
     * @covers ::addKeyword
330
     * @uses phpDocumentor\Reflection\Types\Context
331
     * @expectedException \InvalidArgumentException
332
     */
333
    public function testAddingAKeywordFailsIfTypeClassDoesNotExist()
334
    {
335
        $fixture = new TypeResolver();
336
        $fixture->addKeyword('mock', 'IDoNotExist');
337
    }
338
339
    /**
340
     * @covers ::__construct
341
     * @covers ::addKeyword
342
     * @uses phpDocumentor\Reflection\Types\Context
343
     * @expectedException \InvalidArgumentException
344
     */
345
    public function testAddingAKeywordFailsIfTypeClassDoesNotImplementTypeInterface()
346
    {
347
        $fixture = new TypeResolver();
348
        $fixture->addKeyword('mock', 'stdClass');
349
    }
350
351
    /**
352
     * @covers ::__construct
353
     * @covers ::resolve
354
     * @uses phpDocumentor\Reflection\Types\Context
355
     *
356
     * @expectedException \InvalidArgumentException
357
     */
358
    public function testExceptionIsThrownIfTypeIsEmpty()
359
    {
360
        $fixture = new TypeResolver();
361
        $fixture->resolve(' ', new Context(''));
362
    }
363
364
    /**
365
     * @covers ::__construct
366
     * @covers ::resolve
367
     * @uses phpDocumentor\Reflection\Types\Context
368
     *
369
     * @expectedException \InvalidArgumentException
370
     */
371
    public function testExceptionIsThrownIfTypeIsNotAString()
372
    {
373
        $fixture = new TypeResolver();
374
        $fixture->resolve(['a'], new Context(''));
375
    }
376
377
    /**
378
     * Returns a list of keywords and expected classes that are created from them.
379
     *
380
     * @return string[][]
381
     */
382
    public function provideKeywords()
383
    {
384
        return [
385
            ['string', 'phpDocumentor\Reflection\Types\String_'],
386
            ['int', 'phpDocumentor\Reflection\Types\Integer'],
387
            ['integer', 'phpDocumentor\Reflection\Types\Integer'],
388
            ['float', 'phpDocumentor\Reflection\Types\Float_'],
389
            ['double', 'phpDocumentor\Reflection\Types\Float_'],
390
            ['bool', 'phpDocumentor\Reflection\Types\Boolean'],
391
            ['boolean', 'phpDocumentor\Reflection\Types\Boolean'],
392
            ['resource', 'phpDocumentor\Reflection\Types\Resource'],
393
            ['null', 'phpDocumentor\Reflection\Types\Null_'],
394
            ['callable', 'phpDocumentor\Reflection\Types\Callable_'],
395
            ['callback', 'phpDocumentor\Reflection\Types\Callable_'],
396
            ['array', 'phpDocumentor\Reflection\Types\Array_'],
397
            ['scalar', 'phpDocumentor\Reflection\Types\Scalar'],
398
            ['object', 'phpDocumentor\Reflection\Types\Object_'],
399
            ['mixed', 'phpDocumentor\Reflection\Types\Mixed'],
400
            ['void', 'phpDocumentor\Reflection\Types\Void_'],
401
            ['$this', 'phpDocumentor\Reflection\Types\This'],
402
            ['static', 'phpDocumentor\Reflection\Types\Static_'],
403
            ['self', 'phpDocumentor\Reflection\Types\Self_'],
404
            ['iterable', Iterable_::class],
405
        ];
406
    }
407
408
    /**
409
     * Provides a list of FQSENs to test the resolution patterns with.
410
     *
411
     * @return string[][]
412
     */
413
    public function provideFqcn()
414
    {
415
        return [
416
            'namespace' => ['\phpDocumentor\Reflection'],
417
            'class'     => ['\phpDocumentor\Reflection\DocBlock'],
418
        ];
419
    }
420
}
421