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

Transformer/Writer/Xml/TagConverterTest.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\Transformer\Writer\Xml;
13
14
use Mockery as m;
15
use phpDocumentor\Descriptor\TagDescriptor;
16
use phpDocumentor\Reflection\Fqsen;
17
use phpDocumentor\Reflection\Types\Compound;
18
use phpDocumentor\Reflection\Types\Integer;
19
use phpDocumentor\Reflection\Types\Object_;
20
use phpDocumentor\Reflection\Types\String_;
21
22
/**
23
 * Test class for \phpDocumentor\Transformer\Writer\Xml\TagConverter.
24
 *
25
 * @coversDefaultClass \phpDocumentor\Transformer\Writer\Xml\TagConverter
26
 */
27
class TagConverterTest extends \Mockery\Adapter\Phpunit\MockeryTestCase
28
{
29
    const TEST_LINENUMBER = 100;
30
31
    /**
32
     * Tests whether the information common to all tags is stored on an XML element.
33
     *
34
     * @param string $name              Name of the tag as provided by the Descriptor.
35
     * @param string $description       Description for the tag as provided by the Descriptor.
36
     * @param string $resultName        Expected resulting name in the XML Element.
37
     * @param string $resultDescription Expected resulting description in the XML Element.
38
     *
39
     * @dataProvider provideTestGenericTag
40
     * @covers ::convert
41
     * @covers ::getDescription
42
     */
43
    public function testConvertGenericTag($name, $description, $resultName, $resultDescription)
44
    {
45
        // Arrange
46
        $tagConverter = new TagConverter();
47
        $parent = $this->prepareDocBlockXMLElement();
48
        $tag = $this->createTagDescriptorMock($name, $description);
49
50
        // Act
51
        $convertedElement = $tagConverter->convert($parent, $tag);
52
53
        // Assert
54
        $this->assertSame($resultName, $convertedElement->getAttribute('name'));
55
        $this->assertSame($resultDescription, $convertedElement->getAttribute('description'));
56
        $this->assertSame((string) self::TEST_LINENUMBER, $convertedElement->getAttribute('line'));
57
    }
58
59
    /**
60
     * Tests whether type information is stored when a tag is processed with compound type information.
61
     *
62
     * @covers ::convert
63
     * @covers ::addTypes
64
     */
65
    public function testWhetherTypesAreAddedWhenPresentAndTypeIsCompound()
66
    {
67
        // Arrange
68
        $tagConverter = new TagConverter();
69
        $parent = $this->prepareDocBlockXMLElement();
70
        $tag = $this->createTagDescriptorMock('name', 'description', 'Tag\VarDescriptor');
71
        $tag->shouldReceive('getType')->andReturn(
72
            new Compound([new String_(), new Integer(), new Object_(new Fqsen('\DateTime'))])
73
        );
74
75
        // Act
76
        $convertedElement = $tagConverter->convert($parent, $tag);
77
78
        // Assert
79
        $types = $convertedElement->getElementsByTagName('type');
80
        $this->assertSame(3, $types->length);
81
        $this->assertSame('string', $types->item(0)->nodeValue);
82
        $this->assertSame('int', $types->item(1)->nodeValue);
83
        $this->assertSame('\DateTime', $types->item(2)->nodeValue);
84
        $this->assertSame('string|int|\DateTime', $convertedElement->getAttribute('type'));
85
    }
86
87
    /**
88
     * Tests whether type information is stored when a tag is processed with information of a single type.
89
     *
90
     * @covers ::convert
91
     * @covers ::addTypes
92
     */
93
    public function testWhetherTypesAreAddedWhenPresentAndTypeIsSingular()
94
    {
95
        // Arrange
96
        $tagConverter = new TagConverter();
97
        $parent = $this->prepareDocBlockXMLElement();
98
        $tag = $this->createTagDescriptorMock('name', 'description', 'Tag\VarDescriptor');
99
        $tag->shouldReceive('getType')->andReturn(new String_());
0 ignored issues
show
The method shouldReceive does only exist in Mockery\MockInterface, but not in phpDocumentor\Descriptor\TagDescriptor.

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...
100
101
        // Act
102
        $convertedElement = $tagConverter->convert($parent, $tag);
0 ignored issues
show
It seems like $tag defined by $this->createTagDescript..., 'Tag\\VarDescriptor') on line 98 can also be of type object<Mockery\MockInterface>; however, phpDocumentor\Transforme...TagConverter::convert() does only seem to accept object<phpDocumentor\Descriptor\TagDescriptor>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
103
104
        // Assert
105
        $types = $convertedElement->getElementsByTagName('type');
106
        $this->assertSame(1, $types->length);
107
        $this->assertSame('string', $convertedElement->getAttribute('type'));
108
    }
109
110
    /**
111
     * Tests whether the variable name is stored for tags containing variable names.
112
     *
113
     * @covers ::convert
114
     * @covers ::addTypes
115
     */
116
    public function testWhetherVariableNamesAreAddedWhenPresent()
117
    {
118
        // Arrange
119
        $tagConverter = new TagConverter();
120
        $parent = $this->prepareDocBlockXMLElement();
121
        $tag = $this->createTagDescriptorMock('name', 'description', 'Tag\VarDescriptor');
122
        $tag->shouldReceive('getType')->andReturn(null);
123
        $tag->shouldReceive('getVariableName')->andReturn('varName');
124
125
        // Act
126
        $convertedElement = $tagConverter->convert($parent, $tag);
127
128
        // Assert
129
        $this->assertSame('varName', $convertedElement->getAttribute('variable'));
130
    }
131
132
    /**
133
     * Tests whether the version number is prepended to the description when version information is available.
134
     *
135
     * @covers ::convert
136
     * @covers ::getDescription
137
     *
138
     * @todo this should be dealt with in a template and not in the code! This activity should be removed and the
139
     * templates updated.
140
     */
141
    public function testWhetherTheVersionIsPrependedToTheDescription()
142
    {
143
        // Arrange
144
        $tagConverter = new TagConverter();
145
        $parent = $this->prepareDocBlockXMLElement();
146
        $tag = $this->createTagDescriptorMock('name', 'description', 'Tag\VersionDescriptor');
147
        $tag->shouldReceive('getVersion')->andReturn('1.0');
148
149
        // Act
150
        $convertedElement = $tagConverter->convert($parent, $tag);
151
152
        // Assert
153
        $this->assertSame('1.0 description', $convertedElement->getAttribute('description'));
154
    }
155
156
    /**
157
     * Tests whether a reference to another element is stored with the tag when such is present.
158
     *
159
     * @covers ::convert
160
     */
161
    public function testWhetherReferencesAreAddedWhenPresent()
162
    {
163
        // Arrange
164
        $reference = '\DateTime::add()';
165
        $tagConverter = new TagConverter();
166
        $parent = $this->prepareDocBlockXMLElement();
167
        $tag = $this->createTagDescriptorMock('name', 'description', 'Tag\UsesDescriptor');
168
        $tag->shouldReceive('getReference')->andReturn($reference);
169
170
        // Act
171
        $convertedElement = $tagConverter->convert($parent, $tag);
172
173
        // Assert
174
        $this->assertSame($reference, $convertedElement->getAttribute('link'));
175
    }
176
177
    /**
178
     * Tests whether a link to a URL is stored with the tag when such is present.
179
     *
180
     * @covers ::convert
181
     */
182
    public function testWhetherLinksAreAddedWhenPresent()
183
    {
184
        // Arrange
185
        $link = 'http://www.phpdoc.org';
186
        $tagConverter = new TagConverter();
187
        $parent = $this->prepareDocBlockXMLElement();
188
        $tag = $this->createTagDescriptorMock('name', 'description', 'Tag\LinkDescriptor');
189
        $tag->shouldReceive('getLink')->andReturn($link);
190
191
        // Act
192
        $convertedElement = $tagConverter->convert($parent, $tag);
193
194
        // Assert
195
        $this->assertSame($link, $convertedElement->getAttribute('link'));
196
    }
197
198
    /**
199
     * Tests whether a method name to another element is stored with the tag when such is present.
200
     *
201
     * @covers ::convert
202
     */
203
    public function testWhetherMethodNamesAreAddedWhenPresent()
204
    {
205
        // Arrange
206
        $methodName = 'getMethod';
207
        $tagConverter = new TagConverter();
208
        $parent = $this->prepareDocBlockXMLElement();
209
        $tag = $this->createTagDescriptorMock('name', 'description', 'Tag\MethodDescriptor');
210
        $tag->shouldReceive('getMethodName')->andReturn($methodName);
211
212
        // Act
213
        $convertedElement = $tagConverter->convert($parent, $tag);
214
215
        // Assert
216
        $this->assertSame($methodName, $convertedElement->getAttribute('method_name'));
217
    }
218
219
    /**
220
     * Provides a test name and description for the generic test.
221
     *
222
     * @see testConvertGenericTag
223
     *
224
     * @return string[][]
225
     */
226
    public function provideTestGenericTag()
227
    {
228
        return [
229
            ['name', 'description', 'name', 'description'],
230
            ['name&test', 'description&test', 'name&amp;test', 'description&amp;test'],
231
        ];
232
    }
233
234
    /**
235
     * Creates an XML Element that can serve as parent.
236
     *
237
     * @return \DOMElement
238
     */
239
    protected function prepareDocBlockXMLElement()
240
    {
241
        $document = new \DOMDocument();
242
        $parent = new \DOMElement('parent');
243
        $document->appendChild($parent);
244
        $parent->setAttribute('line', self::TEST_LINENUMBER);
245
246
        return $parent;
247
    }
248
249
    /**
250
     * Creates a mock for the TagDescriptor class.
251
     *
252
     * @param string $name        The name of the tag.
253
     * @param string $description The description that is present in the tag.
254
     * @param string $class       The descriptor class that is to be mocked
255
     *
256
     * @return m\MockInterface|TagDescriptor
257
     */
258
    protected function createTagDescriptorMock($name, $description, $class = 'TagDescriptor')
259
    {
260
        $tag = m::mock('phpDocumentor\\Descriptor\\' . $class);
261
        $tag->shouldReceive('getName')->andReturn($name);
262
        $tag->shouldReceive('getDescription')->andReturn($description);
263
        $tag->shouldIgnoreMissing();
264
265
        return $tag;
266
    }
267
}
268