Completed
Push — develop ( 4b49c4...89d32a )
by Jaap
09:06 queued 05:30
created

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

Labels
Severity

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-2013 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\DescriptorAbstract;
18
use phpDocumentor\Descriptor\MethodDescriptor;
19
use phpDocumentor\Descriptor\NamespaceDescriptor;
20
use phpDocumentor\Descriptor\Tag\SeeDescriptor;
21
use phpDocumentor\Descriptor\Type\UnknownTypeDescriptor;
22
23
/**
24
 * Tests the functionality for the Linker class.
25
 *
26
 * @coversDefaultClass phpDocumentor\Compiler\Linker\Linker
27
 */
28
class LinkerTest extends \Mockery\Adapter\Phpunit\MockeryTestCase
29
{
30
    /**
31
     * @covers ::setObjectAliasesList
32
     * @covers ::findAlias
33
     * @covers ::isContextMarkerInFqsen()
34
     * @covers ::fetchElementByFqsen()
35
     * @covers ::getTypeWithClassAsContext()
36
     */
37
    public function testFindObjectAliasWithFqsenWhenContextIsClass()
38
    {
39
        $object = new \stdClass();
40
        $fqsenWithContextMarker  = '@context::MyMethod()';
41
        $fqsen  = '\phpDocumentor\Descriptor\MyClass::MyMethod()';
42
        $container = m::mock('phpDocumentor\Descriptor\ClassDescriptor');
43
        $container->shouldReceive('getFullyQualifiedStructuralElementName')
44
            ->andReturn('\phpDocumentor\Descriptor\MyClass');
45
        $container->shouldReceive('getNamespace')->andReturn('\phpDocumentor\Descriptor');
46
47
        $linker = new Linker(array());
48
        $linker->setObjectAliasesList(array($fqsen => $object));
49
50
        $this->assertSame($object, $linker->findAlias($fqsenWithContextMarker, $container));
51
    }
52
53
    /**
54
     * @covers ::setObjectAliasesList
55
     * @covers ::findAlias
56
     * @covers ::isContextMarkerInFqsen()
57
     * @covers ::fetchElementByFqsen()
58
     * @covers ::getTypeWithNamespaceAsContext()
59
     */
60
    public function testFindObjectAliasWithFqsenAndContainerWhenContextIsContainerNamespace()
61
    {
62
        $object = new \stdClass();
63
        $fqsenWithContextMarker  = '@context::MyClass';
64
        $fqsen = '\phpDocumentor\Descriptor\MyClass';
65
        $container = m::mock('phpDocumentor\Descriptor\DescriptorAbstract');
66
        $container->shouldReceive('getFullyQualifiedStructuralElementName')->andReturn('\phpDocumentor\Descriptor');
67
        $container->shouldReceive('getNamespace')->andReturn('\phpDocumentor\Descriptor');
68
69
        $linker = new Linker(array());
70
        $linker->setObjectAliasesList(array($fqsen => $object));
71
72
        $this->assertSame($object, $linker->findAlias($fqsenWithContextMarker, $container));
73
    }
74
75
    /**
76
     * @covers ::setObjectAliasesList
77
     * @covers ::findAlias
78
     * @covers ::isContextMarkerInFqsen()
79
     * @covers ::fetchElementByFqsen()
80
     * @covers ::getTypeWithGlobalNamespaceAsContext()
81
     */
82
    public function testFindObjectAliasWithFqsenAndContainerWhenContextIsGlobalNamespace()
83
    {
84
        $object = new \stdClass();
85
        $fqsenWithContextMarker  = '@context::MyClass';
86
        $fqsen = '\MyClass';
87
        $container = m::mock('phpDocumentor\Descriptor\DescriptorAbstract');
88
        $container->shouldReceive('getFullyQualifiedStructuralElementName')->andReturn('\phpDocumentor\Descriptor');
89
        $container->shouldReceive('getNamespace')->andReturn('\phpDocumentor\Descriptor');
90
91
        $linker = new Linker(array());
92
        $linker->setObjectAliasesList(array($fqsen => $object));
93
94
        $this->assertSame($object, $linker->findAlias($fqsenWithContextMarker, $container));
95
    }
96
97
    /**
98
     * @covers ::findAlias
99
     * @covers ::isContextMarkerInFqsen()
100
     * @covers ::fetchElementByFqsen()
101
     * @covers ::getTypeWithClassAsContext()
102
     * @covers ::getTypeWithNamespaceAsContext()
103
     * @covers ::getTypeWithGlobalNamespaceAsContext()
104
     */
105
    public function testFindObjectAliasReturnsNamespaceContextWhenElementIsUndocumented()
106
    {
107
        $fqsenWithContextMarker  = '@context::MyClass';
108
        $container = m::mock('phpDocumentor\Descriptor\NamespaceDescriptor');
109
        $container->shouldReceive('getFullyQualifiedStructuralElementName')->andReturn('\phpDocumentor\Descriptor');
110
        $container->shouldReceive('getNamespace')->andReturn('\phpDocumentor\Descriptor');
111
112
        $linker = new Linker(array());
113
114
        $this->assertSame(
115
            '\phpDocumentor\Descriptor\MyClass',
116
            $linker->findAlias($fqsenWithContextMarker, $container)
117
        );
118
    }
119
120
    /**
121
     * @covers ::findAlias
122
     */
123
    public function testFindObjectAliasReturnsNothingWithUnknownFqsen()
124
    {
125
        $linker = new Linker(array());
126
127
        $this->assertNull($linker->findAlias('\phpDocumentor\MyClass'));
128
    }
129
130
    /**
131
     * @covers ::findFieldValue
132
     */
133
    public function testFindFqsenInObject()
134
    {
135
        $fieldName = 'field';
136
        $fqsen     = '\phpDocumentor\MyClass';
137
138
        $object = m::mock('stdClass');
139
        $object->shouldReceive('getField')->andReturn($fqsen);
140
141
        $linker = new Linker(array());
142
143
        $this->assertSame($fqsen, $linker->findFieldValue($object, $fieldName));
144
    }
145
146
    /**
147
     * @covers ::__construct
148
     * @covers ::getSubstitutions
149
     */
150
    public function testSetFieldsToSubstitute()
151
    {
152
        $elementList = array(
153
            'phpDocumentor\Descriptor\ProjectDescriptor' => 'files',
154
            'phpDocumentor\Descriptor\FileDescriptor'    => 'classes',
155
            'phpDocumentor\Descriptor\ClassDescriptor'   => 'parent'
156
        );
157
        $linker = new Linker($elementList);
158
159
        $this->assertSame($elementList, $linker->getSubstitutions());
160
    }
161
162
    /**
163
     * @covers ::__construct
164
     * @covers ::substitute
165
     * @covers ::isDescriptorContainer()
166
     */
167
    public function testSubstituteFqsenInObject()
168
    {
169
        // initialize parameters
170
        $result    = new \stdClass();
171
        $fieldName = 'field';
172
173
        list($object, $fqsen) = $this->createMockDescriptorForResult($result);
174
175
        // prepare linker
176
        $linker = new Linker(array($fqsen => array($fieldName)));
177
        $linker->setObjectAliasesList(array($fqsen => $result));
178
179
        // execute test.
180
        $linker->substitute($object);
181
182
        // mark test as successful due to asserts in Mockery
183
        $this->assertTrue(true);
184
    }
185
186
    /**
187
     * @covers ::__construct
188
     * @covers ::substitute
189
     * @covers ::isDescriptorContainer()
190
     */
191
    public function testSubstituteFqsenInUnknownTypeDescriptor()
192
    {
193
        // initialize parameters
194
        $result    = new \stdClass();
195
        $fieldName = 'field';
196
197
        list($object, $fqsen) = $this->createMockUnknownTypeDescriptorForResult($result);
198
199
        // prepare linker
200
        $linker = new Linker(array($fqsen => array($fieldName)));
201
        $linker->setObjectAliasesList(array($fqsen => $result));
202
203
        // execute test.
204
        $linker->substitute($object);
205
206
        // mark test as successful due to asserts in Mockery
207
        $this->assertTrue(true);
208
    }
209
210
    /**
211
     * @covers ::__construct
212
     * @covers ::substitute
213
     * @depends testSubstituteFqsenInObject
214
     */
215
    public function testMultipleSubstitutionsInOneObject()
216
    {
217
        // initialize parameters
218
        $result     = new \stdClass();
219
        $fieldNames  = array('field1', 'field2');
220
221
        // assert that the getField is called (and returns a FQSEN) and the setField is called with the expected object
222
        $object = m::mock('stdClass');
223
        $fqsen  = get_class($object);
224
        foreach (array_keys($fieldNames) as $index) {
225
            $object->shouldReceive('getField' . ($index + 1))->atLeast()->once()->andReturn($fqsen);
226
            $object->shouldReceive('setField' . ($index + 1))->atLeast()->once()->with($result);
227
        }
228
229
        // prepare linker
230
        $linker = new Linker(array($fqsen => $fieldNames));
231
        $linker->setObjectAliasesList(array($fqsen => $result));
232
233
        // execute test.
234
        $linker->substitute($object);
235
236
        // mark test as successful due to asserts in Mockery
237
        $this->assertTrue(true);
238
    }
239
240
    /**
241
     * @covers ::substitute
242
     * @depends testSubstituteFqsenInObject
243
     */
244
    public function testSubstituteFieldsViaChildObject()
245
    {
246
        // initialize parameters
247
        $result         = new \stdClass();
248
        $childFieldName = 'field';
249
        $fieldName      = 'child';
250
251
        list($childObject, $childFqsen) = $this->createMockDescriptorForResult($result);
252
253
        $object = m::mock('phpDocumentor\Descripto\DescriptorAbstract');
254
        $fqsen  = get_class($object);
255
        $object->shouldReceive('getChild')->atLeast()->once()->andReturn($childObject);
256
        $object->shouldReceive('setChild')->never();
257
258
        // prepare linker
259
        $linker = new Linker(
260
            array(
261
                 $fqsen => array($fieldName),
262
                 $childFqsen => array($childFieldName)
263
            )
264
        );
265
        $linker->setObjectAliasesList(array($childFqsen => $result));
266
267
        // execute test.
268
        $linker->substitute($object);
269
270
        // mark test as successful due to asserts in Mockery
271
        $this->assertTrue(true);
272
    }
273
274
    /**
275
     * @covers ::substitute
276
     * @depends testSubstituteFqsenInObject
277
     */
278
    public function testSubstituteFieldsViaArrayOfChildObjects()
279
    {
280
        // initialize parameters
281
        $result         = new \stdClass();
282
        $childFieldName = 'field';
283
        $fieldName      = 'child';
284
285
        list($childObject, $childFqsen) = $this->createMockDescriptorForResult($result);
286
287
        $object = m::mock('phpDocumentor\Descriptor\DescriptorAbstract');
288
        $fqsen  = get_class($object);
289
        $object->shouldReceive('getChild')->atLeast()->once()->andReturn(array($childObject));
290
        $object->shouldReceive('setChild');
291
292
        // prepare linker
293
        $linker = new Linker(
294
            array(
295
                 $fqsen => array($fieldName),
296
                 $childFqsen => array($childFieldName)
297
            )
298
        );
299
        $linker->setObjectAliasesList(array($childFqsen => $result));
300
301
        // execute test.
302
        $linker->substitute($object);
303
304
        // mark test as successful due to asserts in Mockery
305
        $this->assertTrue(true);
306
    }
307
308
    /**
309
     * @covers ::substitute
310
     */
311
    public function testSubstituteArrayRecursive()
312
    {
313
        $mock = m::mock('phpDocumentor\Compiler\Linker\Linker');
314
        $mock->shouldDeferMissing();
315
        $mock->shouldReceive('findAlias')->andReturn('substituted');
0 ignored issues
show
The method andReturn does only exist in Mockery\ExpectationInterface, but not in Mockery\HigherOrderMessage.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
316
        $elementList = array(
317
            'one' => array('two' => 'two'),
318
        );
319
        $result = $mock->substitute($elementList);
0 ignored issues
show
The method substitute() does not seem to exist on object<Mockery\MockInterface>.

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...
320
        $expected = array(
321
            'one' => array('two' => 'substituted'),
322
        );
323
        $this->assertSame($expected, $result);
324
    }
325
326
    /**
327
     * Test that already processed objects don't substitute again
328
     * Using mockery, as return value would be `null` in both cases
329
     *
330
     * @covers ::substitute
331
     */
332
    public function testSubstituteSkipProcessed()
333
    {
334
        $mock = m::mock('phpDocumentor\Compiler\Linker\Linker');
335
        $mock->shouldDeferMissing();
336
        $mock->shouldReceive('findFieldValue')->atMost()->once();
337
338
        $item = new \stdClass();
339
        $item->attribute = 'foreachme';
340
341
        //findFieldValue() should be called
342
        $result = $mock->substitute($item);
0 ignored issues
show
The method substitute() does not seem to exist on object<Mockery\MockInterface>.

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...
343
344
        //findFieldvalue() should NOT be called
345
        $result = $mock->substitute($item);
0 ignored issues
show
The method substitute() does not seem to exist on object<Mockery\MockInterface>.

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...
346
347
        // mark test as successful due to asserts in Mockery
348
        $this->assertTrue(true);
349
    }
350
351
    /**
352
     * @covers ::getDescription
353
     */
354
    public function testGetDescription()
355
    {
356
        $linker = new Linker(array());
357
        $expected = 'Replace textual FQCNs with object aliases';
358
        $this->assertSame($expected, $linker->getDescription());
359
    }
360
361
    /**
362
     * @covers ::execute
363
     */
364
    public function testExecute()
365
    {
366
        $std = m::mock('stdClass');
367
        $std->shouldReceive('getAll')->andReturn(array());
368
        $indexes = new \stdClass();
369
        $indexes->elements = $std;
370
        $descriptor = m::mock('phpDocumentor\Descriptor\ProjectDescriptor');
371
        $descriptor->shouldReceive('getIndexes')->andReturn($indexes);
372
373
        $mock = m::mock('phpDocumentor\Compiler\Linker\Linker');
374
        $mock->shouldDeferMissing();
375
        $mock->shouldReceive('substitute')->with($descriptor);
376
        $mock->execute($descriptor);
0 ignored issues
show
The method execute() does not seem to exist on object<Mockery\MockInterface>.

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...
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
            array(
390
                'phpDocumentor\Descriptor\ClassDescriptor'   => array('methods'),
391
                'phpDocumentor\Descriptor\MethodDescriptor'  => array('tags'),
392
                'phpDocumentor\Descriptor\Tag\SeeDescriptor' => array('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
            array(
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
            array(
426
                'phpDocumentor\Descriptor\ClassDescriptor'   => array('methods'),
427
                'phpDocumentor\Descriptor\MethodDescriptor'  => array('tags'),
428
                'phpDocumentor\Descriptor\Tag\SeeDescriptor' => array('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
            array(
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 $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 array($object, $fqsen);
472
    }
473
474
    /**
475
     * @param $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 array($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 ClassDescriptor $classDescriptor
511
     * @param string          $methodName
512
     *
513
     * @return MethodDescriptor
514
     */
515
    private function givenAMethodWithClassAndName(ClassDescriptor $classDescriptor, $methodName)
516
    {
517
        $methodDescriptor = new MethodDescriptor();
518
        $methodDescriptor->setName($methodName);
519
        $methodDescriptor->setFullyQualifiedStructuralElementName($classDescriptor . '::' . $methodName . '()');
520
521
        return $methodDescriptor;
522
    }
523
524
    /**
525
     * Returns a SeeDescriptor with its reference set.
526
     *
527
     * @param $reference
528
     *
529
     * @return SeeDescriptor
530
     */
531
    private function givenASeeDescriptorWithReference($reference)
532
    {
533
        $seeDescriptor = new SeeDescriptor('see');
534
        $seeDescriptor->setReference($reference);
535
536
        return $seeDescriptor;
537
    }
538
}
539