Completed
Push — develop ( 8eb671...133594 )
by Mike
19:30 queued 09:24
created

unit/phpDocumentor/Compiler/Linker/LinkerTest.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * phpDocumentor
4
 *
5
 * PHP Version 5.3
6
 *
7
 * @copyright 2010-2018 Mike van Riel / Naenius (http://www.naenius.com)
8
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
9
 * @link      http://phpdoc.org
10
 */
11
12
namespace phpDocumentor\Compiler\Linker;
13
14
use Mockery as m;
15
use phpDocumentor\Descriptor\ClassDescriptor;
16
use phpDocumentor\Descriptor\Collection;
17
use phpDocumentor\Descriptor\MethodDescriptor;
18
use phpDocumentor\Descriptor\Tag\SeeDescriptor;
19
20
/**
21
 * Tests the functionality for the Linker class.
22
 *
23
 * @coversDefaultClass phpDocumentor\Compiler\Linker\Linker
24
 */
25
class LinkerTest extends \Mockery\Adapter\Phpunit\MockeryTestCase
26
{
27
    /**
28
     * @covers ::setObjectAliasesList
29
     * @covers ::findAlias
30
     * @covers ::isContextMarkerInFqsen()
31
     * @covers ::fetchElementByFqsen()
32
     * @covers ::getTypeWithClassAsContext()
33
     */
34
    public function testFindObjectAliasWithFqsenWhenContextIsClass()
35
    {
36
        $object = new \stdClass();
37
        $fqsenWithContextMarker = '@context::MyMethod()';
38
        $fqsen = '\phpDocumentor\Descriptor\MyClass::MyMethod()';
39
        $container = m::mock('phpDocumentor\Descriptor\ClassDescriptor');
40
        $container->shouldReceive('getFullyQualifiedStructuralElementName')
41
            ->andReturn('\phpDocumentor\Descriptor\MyClass');
42
        $container->shouldReceive('getNamespace')->andReturn('\phpDocumentor\Descriptor');
43
44
        $linker = new Linker([]);
45
        $linker->setObjectAliasesList([$fqsen => $object]);
46
47
        $this->assertSame($object, $linker->findAlias($fqsenWithContextMarker, $container));
48
    }
49
50
    /**
51
     * @covers ::setObjectAliasesList
52
     * @covers ::findAlias
53
     * @covers ::isContextMarkerInFqsen()
54
     * @covers ::fetchElementByFqsen()
55
     * @covers ::getTypeWithNamespaceAsContext()
56
     */
57
    public function testFindObjectAliasWithFqsenAndContainerWhenContextIsContainerNamespace()
58
    {
59
        $object = new \stdClass();
60
        $fqsenWithContextMarker = '@context::MyClass';
61
        $fqsen = '\phpDocumentor\Descriptor\MyClass';
62
        $container = m::mock('phpDocumentor\Descriptor\DescriptorAbstract');
63
        $container->shouldReceive('getFullyQualifiedStructuralElementName')->andReturn('\phpDocumentor\Descriptor');
64
        $container->shouldReceive('getNamespace')->andReturn('\phpDocumentor\Descriptor');
65
66
        $linker = new Linker([]);
67
        $linker->setObjectAliasesList([$fqsen => $object]);
68
69
        $this->assertSame($object, $linker->findAlias($fqsenWithContextMarker, $container));
70
    }
71
72
    /**
73
     * @covers ::setObjectAliasesList
74
     * @covers ::findAlias
75
     * @covers ::isContextMarkerInFqsen()
76
     * @covers ::fetchElementByFqsen()
77
     * @covers ::getTypeWithGlobalNamespaceAsContext()
78
     */
79
    public function testFindObjectAliasWithFqsenAndContainerWhenContextIsGlobalNamespace()
80
    {
81
        $object = new \stdClass();
82
        $fqsenWithContextMarker = '@context::MyClass';
83
        $fqsen = '\MyClass';
84
        $container = m::mock('phpDocumentor\Descriptor\DescriptorAbstract');
85
        $container->shouldReceive('getFullyQualifiedStructuralElementName')->andReturn('\phpDocumentor\Descriptor');
86
        $container->shouldReceive('getNamespace')->andReturn('\phpDocumentor\Descriptor');
87
88
        $linker = new Linker([]);
89
        $linker->setObjectAliasesList([$fqsen => $object]);
90
91
        $this->assertSame($object, $linker->findAlias($fqsenWithContextMarker, $container));
92
    }
93
94
    /**
95
     * @covers ::findAlias
96
     * @covers ::isContextMarkerInFqsen()
97
     * @covers ::fetchElementByFqsen()
98
     * @covers ::getTypeWithClassAsContext()
99
     * @covers ::getTypeWithNamespaceAsContext()
100
     * @covers ::getTypeWithGlobalNamespaceAsContext()
101
     */
102
    public function testFindObjectAliasReturnsNamespaceContextWhenElementIsUndocumented()
103
    {
104
        $fqsenWithContextMarker = '@context::MyClass';
105
        $container = m::mock('phpDocumentor\Descriptor\NamespaceDescriptor');
106
        $container->shouldReceive('getFullyQualifiedStructuralElementName')->andReturn('\phpDocumentor\Descriptor');
107
        $container->shouldReceive('getNamespace')->andReturn('\phpDocumentor\Descriptor');
108
109
        $linker = new Linker([]);
110
111
        $this->assertSame(
112
            '\phpDocumentor\Descriptor\MyClass',
113
            $linker->findAlias($fqsenWithContextMarker, $container)
114
        );
115
    }
116
117
    /**
118
     * @covers ::findAlias
119
     */
120
    public function testFindObjectAliasReturnsNothingWithUnknownFqsen()
121
    {
122
        $linker = new Linker([]);
123
124
        $this->assertNull($linker->findAlias('\phpDocumentor\MyClass'));
125
    }
126
127
    /**
128
     * @covers ::findFieldValue
129
     */
130
    public function testFindFqsenInObject()
131
    {
132
        $fieldName = 'field';
133
        $fqsen = '\phpDocumentor\MyClass';
134
135
        $object = m::mock('stdClass');
136
        $object->shouldReceive('getField')->andReturn($fqsen);
137
138
        $linker = new Linker([]);
139
140
        $this->assertSame($fqsen, $linker->findFieldValue($object, $fieldName));
141
    }
142
143
    /**
144
     * @covers ::__construct
145
     * @covers ::getSubstitutions
146
     */
147
    public function testSetFieldsToSubstitute()
148
    {
149
        $elementList = [
150
            'phpDocumentor\Descriptor\ProjectDescriptor' => 'files',
151
            'phpDocumentor\Descriptor\FileDescriptor' => 'classes',
152
            'phpDocumentor\Descriptor\ClassDescriptor' => 'parent',
153
        ];
154
        $linker = new Linker($elementList);
155
156
        $this->assertSame($elementList, $linker->getSubstitutions());
157
    }
158
159
    /**
160
     * @covers ::__construct
161
     * @covers ::substitute
162
     * @covers ::isDescriptorContainer()
163
     */
164
    public function testSubstituteFqsenInObject()
165
    {
166
        // initialize parameters
167
        $result = new \stdClass();
168
        $fieldName = 'field';
169
170
        list($object, $fqsen) = $this->createMockDescriptorForResult($result);
171
172
        // prepare linker
173
        $linker = new Linker([$fqsen => [$fieldName]]);
174
        $linker->setObjectAliasesList([$fqsen => $result]);
175
176
        // execute test.
177
        $linker->substitute($object);
178
179
        // mark test as successful due to asserts in Mockery
180
        $this->assertTrue(true);
181
    }
182
183
    /**
184
     * @covers ::__construct
185
     * @covers ::substitute
186
     * @covers ::isDescriptorContainer()
187
     */
188
    public function testSubstituteFqsenInUnknownTypeDescriptor()
189
    {
190
        // initialize parameters
191
        $result = new \stdClass();
192
        $fieldName = 'field';
193
194
        list($object, $fqsen) = $this->createMockUnknownTypeDescriptorForResult($result);
195
196
        // prepare linker
197
        $linker = new Linker([$fqsen => [$fieldName]]);
198
        $linker->setObjectAliasesList([$fqsen => $result]);
199
200
        // execute test.
201
        $linker->substitute($object);
202
203
        // mark test as successful due to asserts in Mockery
204
        $this->assertTrue(true);
205
    }
206
207
    /**
208
     * @covers ::__construct
209
     * @covers ::substitute
210
     * @depends testSubstituteFqsenInObject
211
     */
212
    public function testMultipleSubstitutionsInOneObject()
213
    {
214
        // initialize parameters
215
        $result = new \stdClass();
216
        $fieldNames = ['field1', 'field2'];
217
218
        // assert that the getField is called (and returns a FQSEN) and the setField is called with the expected object
219
        $object = m::mock('stdClass');
220
        $fqsen = get_class($object);
221
        foreach (array_keys($fieldNames) as $index) {
222
            $object->shouldReceive('getField' . ($index + 1))->atLeast()->once()->andReturn($fqsen);
223
            $object->shouldReceive('setField' . ($index + 1))->atLeast()->once()->with($result);
224
        }
225
226
        // prepare linker
227
        $linker = new Linker([$fqsen => $fieldNames]);
228
        $linker->setObjectAliasesList([$fqsen => $result]);
229
230
        // execute test.
231
        $linker->substitute($object);
232
233
        // mark test as successful due to asserts in Mockery
234
        $this->assertTrue(true);
235
    }
236
237
    /**
238
     * @covers ::substitute
239
     * @depends testSubstituteFqsenInObject
240
     */
241
    public function testSubstituteFieldsViaChildObject()
242
    {
243
        // initialize parameters
244
        $result = new \stdClass();
245
        $childFieldName = 'field';
246
        $fieldName = 'child';
247
248
        list($childObject, $childFqsen) = $this->createMockDescriptorForResult($result);
249
250
        $object = m::mock('phpDocumentor\Descripto\DescriptorAbstract');
251
        $fqsen = get_class($object);
252
        $object->shouldReceive('getChild')->atLeast()->once()->andReturn($childObject);
253
        $object->shouldReceive('setChild')->never();
254
255
        // prepare linker
256
        $linker = new Linker(
257
            [
258
                $fqsen => [$fieldName],
259
                $childFqsen => [$childFieldName],
260
            ]
261
        );
262
        $linker->setObjectAliasesList([$childFqsen => $result]);
263
264
        // execute test.
265
        $linker->substitute($object);
266
267
        // mark test as successful due to asserts in Mockery
268
        $this->assertTrue(true);
269
    }
270
271
    /**
272
     * @covers ::substitute
273
     * @depends testSubstituteFqsenInObject
274
     */
275
    public function testSubstituteFieldsViaArrayOfChildObjects()
276
    {
277
        // initialize parameters
278
        $result = new \stdClass();
279
        $childFieldName = 'field';
280
        $fieldName = 'child';
281
282
        list($childObject, $childFqsen) = $this->createMockDescriptorForResult($result);
283
284
        $object = m::mock('phpDocumentor\Descriptor\DescriptorAbstract');
285
        $fqsen = get_class($object);
286
        $object->shouldReceive('getChild')->atLeast()->once()->andReturn([$childObject]);
287
        $object->shouldReceive('setChild');
288
289
        // prepare linker
290
        $linker = new Linker(
291
            [
292
                $fqsen => [$fieldName],
293
                $childFqsen => [$childFieldName],
294
            ]
295
        );
296
        $linker->setObjectAliasesList([$childFqsen => $result]);
297
298
        // execute test.
299
        $linker->substitute($object);
300
301
        // mark test as successful due to asserts in Mockery
302
        $this->assertTrue(true);
303
    }
304
305
    /**
306
     * @covers ::substitute
307
     */
308
    public function testSubstituteArrayRecursive()
309
    {
310
        /** @var Linker|m\MockInterface $mock */
311
        $mock = m::mock('phpDocumentor\Compiler\Linker\Linker');
312
        $mock->shouldDeferMissing();
313
        $mock->shouldReceive('findAlias')->andReturn('substituted');
314
        $elementList = [
315
            'one' => ['two' => 'two'],
316
        ];
317
        $result = $mock->substitute($elementList);
318
        $expected = [
319
            'one' => ['two' => 'substituted'],
320
        ];
321
        $this->assertSame($expected, $result);
322
    }
323
324
    /**
325
     * Test that already processed objects don't substitute again
326
     * Using mockery, as return value would be `null` in both cases
327
     *
328
     * @covers ::substitute
329
     */
330
    public function testSubstituteSkipProcessed()
331
    {
332
        /** @var Linker|m\MockInterface $mock */
333
        $mock = m::mock('phpDocumentor\Compiler\Linker\Linker');
334
        $mock->shouldDeferMissing();
335
        $mock->shouldReceive('findFieldValue')->atMost()->once();
336
337
        $item = new \stdClass();
338
        $item->attribute = 'foreachme';
339
340
        //findFieldValue() should be called
341
        $result = $mock->substitute($item);
0 ignored issues
show
$result is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
342
343
        //findFieldvalue() should NOT be called
344
        $result = $mock->substitute($item);
0 ignored issues
show
$result is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
345
346
        // mark test as successful due to asserts in Mockery
347
        $this->assertTrue(true);
348
    }
349
350
    /**
351
     * @covers ::getDescription
352
     */
353
    public function testGetDescription()
354
    {
355
        $linker = new Linker([]);
356
        $expected = 'Replace textual FQCNs with object aliases';
357
        $this->assertSame($expected, $linker->getDescription());
358
    }
359
360
    /**
361
     * @covers ::execute
362
     */
363
    public function testExecute()
364
    {
365
        $std = m::mock('stdClass');
366
        $std->shouldReceive('getAll')->andReturn([]);
367
        $indexes = new \stdClass();
368
        $indexes->elements = $std;
369
        $descriptor = m::mock('phpDocumentor\Descriptor\ProjectDescriptor');
370
        $descriptor->shouldReceive('getIndexes')->andReturn($indexes);
371
372
        /** @var Linker|m\MockInterface $mock */
373
        $mock = m::mock('phpDocumentor\Compiler\Linker\Linker');
374
        $mock->shouldDeferMissing();
375
        $mock->shouldReceive('substitute')->with($descriptor);
376
        $mock->execute($descriptor);
377
378
        // mark test as successful due to asserts in Mockery
379
        $this->assertTrue(true);
380
    }
381
382
    /**
383
     * @covers ::execute
384
     * @covers ::replacePseudoTypes
385
     */
386
    public function testReplaceSelfWithCurrentClassInScope()
387
    {
388
        $fixture = new Linker(
389
            [
390
                'phpDocumentor\Descriptor\ClassDescriptor' => ['methods'],
391
                'phpDocumentor\Descriptor\MethodDescriptor' => ['tags'],
392
                'phpDocumentor\Descriptor\Tag\SeeDescriptor' => ['reference'],
393
            ]
394
        );
395
396
        $methodName = 'myMethod';
397
        $fqnn = '\My\Space';
398
        $className = 'MyClass';
399
        $seeDescriptor = $this->givenASeeDescriptorWithReference('self::' . $methodName . '()');
400
        $classDescriptor = $this->givenAClassWithNamespaceAndClassName($fqnn, $className);
401
        $methodDescriptor = $this->givenAMethodWithClassAndName($classDescriptor, $methodName);
402
403
        $methodDescriptor->getTags()->get($seeDescriptor->getName(), new Collection())->add($seeDescriptor);
404
        $classDescriptor->getMethods()->add($methodDescriptor);
405
406
        $fixture->setObjectAliasesList(
407
            [
408
                $fqnn . '\\' . $className => $classDescriptor,
409
                $fqnn . '\\' . $className . '::' . $methodName . '()' => $methodDescriptor,
410
            ]
411
        );
412
413
        $fixture->substitute($classDescriptor);
414
415
        $this->assertSame($methodDescriptor, $seeDescriptor->getReference());
416
    }
417
418
    /**
419
     * @covers ::execute
420
     * @covers ::replacePseudoTypes
421
     */
422
    public function testReplaceThisWithCurrentClassInScope()
423
    {
424
        $fixture = new Linker(
425
            [
426
                'phpDocumentor\Descriptor\ClassDescriptor' => ['methods'],
427
                'phpDocumentor\Descriptor\MethodDescriptor' => ['tags'],
428
                'phpDocumentor\Descriptor\Tag\SeeDescriptor' => ['reference'],
429
            ]
430
        );
431
432
        $methodName = 'myMethod';
433
        $fqnn = '\My\Space';
434
        $className = 'MyClass';
435
        $seeDescriptor = $this->givenASeeDescriptorWithReference('$this::' . $methodName . '()');
436
        $classDescriptor = $this->givenAClassWithNamespaceAndClassName($fqnn, $className);
437
        $methodDescriptor = $this->givenAMethodWithClassAndName($classDescriptor, $methodName);
438
439
        $methodDescriptor->getTags()->get($seeDescriptor->getName(), new Collection())->add($seeDescriptor);
440
        $classDescriptor->getMethods()->add($methodDescriptor);
441
442
        $fixture->setObjectAliasesList(
443
            [
444
                $fqnn . '\\' . $className => $classDescriptor,
445
                $fqnn . '\\' . $className . '::' . $methodName . '()' => $methodDescriptor,
446
            ]
447
        );
448
449
        $fixture->substitute($classDescriptor);
450
451
        $this->assertSame($methodDescriptor, $seeDescriptor->getReference());
452
    }
453
454
    /**
455
     * @param \stdClass|null $result
456
     *
457
     * @return array
458
     */
459
    protected function createMockDescriptorForResult($result = null)
460
    {
461
        $object = m::mock('stdClass');
462
        $fqsen = get_class($object);
463
        $object->shouldReceive('getField')->atLeast()->once()->andReturn($fqsen);
464
465
        if ($result) {
466
            $object->shouldReceive('setField')->atLeast()->once()->with($result);
467
        } else {
468
            $object->shouldReceive('setField')->never();
469
        }
470
471
        return [$object, $fqsen];
472
    }
473
474
    /**
475
     * @param \stdClass|null $result
476
     *
477
     * @return array
478
     */
479
    protected function createMockUnknownTypeDescriptorForResult($result = null)
480
    {
481
        $object = m::mock('phpDocumentor\Descriptor\Type\UnknownTypeDescriptor');
482
        $fqsen = get_class($object);
483
484
        $object->shouldReceive('getName')->andReturn('\Name');
485
486
        return [$object, $fqsen];
487
    }
488
489
    /**
490
     * Returns a ClassDescriptor whose namespace and name is set.
491
     *
492
     * @param string $fqnn
493
     * @param string $className
494
     *
495
     * @return ClassDescriptor
496
     */
497
    private function givenAClassWithNamespaceAndClassName($fqnn, $className)
498
    {
499
        $classDescriptor = new ClassDescriptor();
500
        $classDescriptor->setFullyQualifiedStructuralElementName($fqnn . '\\' . $className);
501
        $classDescriptor->setNamespace($fqnn);
502
        $classDescriptor->setName($className);
503
504
        return $classDescriptor;
505
    }
506
507
    /**
508
     * Returns a method whose name is set.
509
     *
510
     * @param string $methodName
511
     * @return MethodDescriptor
512
     */
513
    private function givenAMethodWithClassAndName(ClassDescriptor $classDescriptor, $methodName)
514
    {
515
        $methodDescriptor = new MethodDescriptor();
516
        $methodDescriptor->setName($methodName);
517
        $methodDescriptor->setFullyQualifiedStructuralElementName($classDescriptor . '::' . $methodName . '()');
518
519
        return $methodDescriptor;
520
    }
521
522
    /**
523
     * Returns a SeeDescriptor with its reference set.
524
     *
525
     * @param string $reference
526
     *
527
     * @return SeeDescriptor
528
     */
529
    private function givenASeeDescriptorWithReference($reference)
530
    {
531
        $seeDescriptor = new SeeDescriptor('see');
532
        $seeDescriptor->setReference($reference);
533
534
        return $seeDescriptor;
535
    }
536
}
537