Completed
Push — develop ( 04dbcf...051570 )
by Jaap
09:44
created

ApiContext::classHasMethodWithExpectedCountTag()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 4
dl 0
loc 7
rs 9.4285
c 0
b 0
f 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-2017 Mike van Riel<[email protected]>
9
 *  @license   http://www.opensource.org/licenses/mit-license.php MIT
10
 *  @link      http://phpdoc.org
11
 */
12
namespace phpDocumentor\Behat\Contexts\Ast;
13
14
use Behat\Behat\Context\Context;
15
use Behat\Behat\Hook\Scope\BeforeScenarioScope;
16
use Behat\Gherkin\Node\PyStringNode;
17
use phpDocumentor\Behat\Contexts\EnvironmentContext;
18
use phpDocumentor\Descriptor\ClassDescriptor;
19
use phpDocumentor\Descriptor\Collection;
20
use phpDocumentor\Descriptor\DescriptorAbstract;
21
use phpDocumentor\Descriptor\FileDescriptor;
22
use phpDocumentor\Descriptor\ProjectDescriptor;
23
use phpDocumentor\Descriptor\Tag\VersionDescriptor;
24
use PHPUnit\Framework\Assert;
25
26
class ApiContext implements Context
27
{
28
    /** @var EnvironmentContext */
29
    private $environmentContext;
30
31
    /** @BeforeScenario */
32
    public function gatherContexts(BeforeScenarioScope $scope)
33
    {
34
        $environment = $scope->getEnvironment();
35
36
        $this->environmentContext = $environment->getContext('phpDocumentor\Behat\Contexts\EnvironmentContext');
37
    }
38
39
    /**
40
     * @Then /^the AST has a class named "([^"]*)" in file "([^"]*)"$/
41
     * @throws \Exception
42
     */
43
    public function theASTHasAclassNamedInFile($class, $file)
44
    {
45
        $ast = $this->getAst();
46
47
        /** @var FileDescriptor $fileDescriptor */
48
        $fileDescriptor = $ast->getFiles()->get($file);
49
50
        /** @var ClassDescriptor $classDescriptor */
51
        foreach ($fileDescriptor->getClasses() as $classDescriptor) {
52
            if ($classDescriptor->getName() === $class) {
53
                return;
54
            }
55
        }
56
57
        throw new \Exception(sprintf('Didn\'t find expected class "%s" in "%s"', $class, $file));
58
    }
59
60
    /**
61
     * @Then /^the class named "([^"]*)" is in the default package$/
62
     * @throws \Exception
63
     */
64
    public function theASTHasAClassInDefaultPackage($class)
65
    {
66
        $class = $this->findClassByName($class);
67
68
        Assert::assertEquals('Default', $class->getPackage()->getName());
69
    }
70
71
    /**
72
     * @param $class
73
     * @param $expectedContent
74
     * @Then the class named ":class" has docblock with content:
75
     */
76
    public function classHasDocblockWithContent($class, PyStringNode $expectedContent)
77
    {
78
        $class = $this->findClassByName($class);
79
80
        Assert::assertEquals($expectedContent->getRaw(), $class->getDescription());
81
    }
82
83
    /**
84
     * @param $classFqsen
85
     * @param $docElement
86
     * @param $value
87
     * @Then class ":classFqsen" has :docElement:
88
     * @throws \Exception
89
     */
90
    public function classHasDocblockContent($classFqsen, $docElement, PyStringNode $value)
91
    {
92
        $class = $this->findClassByFqsen($classFqsen);
93
94
        $method = 'get' . $docElement;
95
96
        Assert::assertEquals($value->getRaw(), $class->$method());
97
    }
98
99
    /**
100
     * @param $classFqsen
101
     * @param $value
102
     * @Then class ":classFqsen" has version :value
103
     */
104
    public function classHasVersion($classFqsen, $value)
105
    {
106
        $class = $this->findClassByFqsen($classFqsen);
107
108
        /** @var VersionDescriptor $tag */
109
        foreach ($class->getVersion() as $tag) {
110
            if($tag->getVersion() === $value) {
0 ignored issues
show
Coding Style introduced by
Expected 1 space after IF keyword; 0 found
Loading history...
111
                return;
112
            }
113
        }
114
115
        Assert::fail(sprintf('Didn\'t find expected version "%s"', $value));
116
    }
117
118
    /**
119
     * @param $classFqsen
120
     * @param $tagName
121
     * @Then class ":classFqsen" without tag :tagName
122
     */
123
    public function classWithoutTag($classFqsen, $tagName)
124
    {
125
        $this->classHasTag($classFqsen, $tagName, 0);
126
    }
127
128
    /**
129
     * @param string $classFqsen
130
     * @param string $tagName
131
     * @param int $expectedNumber
132
     * @Then class ":classFqsen" has exactly :expectedNumber tag :tagName
133
     */
134
    public function classHasTag($classFqsen, $tagName, $expectedNumber)
135
    {
136
        $class = $this->findClassByFqsen($classFqsen);
137
        static::AssertTagCount($class, $tagName, $expectedNumber);
0 ignored issues
show
Bug introduced by
Since AssertTagCount() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of AssertTagCount() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
138
    }
139
140
    /**
141
     * @param string $classFqsen
142
     * @param string $tagName
143
     * @param string $method
144
     * @Then class ":classFqsen" has a method named :method without tag :tagName
145
     */
146
    public function classHasMethodWithoutTag($classFqsen, $tagName, $method)
147
    {
148
        $this->classHasMethodWithExpectedCountTag($classFqsen, $tagName, $method, 0);
149
    }
150
151
    /**
152
     * @param string $classFqsen
153
     * @param string $tagName
154
     * @param string $methodName
155
     * @Then class ":classFqsen" has a method named :method with exactly :expected tag :tagName
156
     */
157
    public function classHasMethodWithExpectedCountTag($classFqsen, $tagName, $methodName, $expectedCount)
158
    {
159
        $class = $this->findClassByFqsen($classFqsen);
160
        $method = $class->getMethods()->get($methodName);
161
162
        static::AssertTagCount($method, $tagName, $expectedCount);
0 ignored issues
show
Bug introduced by
Since AssertTagCount() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of AssertTagCount() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
163
    }
164
165
    /**
166
     * @param string $className
167
     * @return ClassDescriptor
168
     * @throws \Exception
169
     */
170
    private function findClassByName($className) {
171
        $ast = $this->getAst();
172
        foreach ($ast->getFiles() as $file) {
173
            foreach ($file->getClasses() as $classDescriptor) {
174
                if ($classDescriptor->getName() === $className) {
175
                    return $classDescriptor;
176
                }
177
            }
178
        }
179
180
        throw new \Exception(sprintf('Didn\'t find expected class "%s"', $className));
181
    }
182
183
    /**
184
     * @param string $classFqsen
185
     * @return ClassDescriptor
186
     * @throws \Exception
187
     */
188
    private function findClassByFqsen($classFqsen)
189
    {
190
        $ast = $this->getAst();
191
        foreach ($ast->getFiles() as $file) {
192
            /** @var ClassDescriptor $classDescriptor */
193
            foreach ($file->getClasses() as $classDescriptor) {
194
                if ($classDescriptor->getFullyQualifiedStructuralElementName() === $classFqsen) {
195
                    return $classDescriptor;
196
                }
197
            }
198
        }
199
200
        throw new \Exception(sprintf('Didn\'t find expected class "%s"', $classFqsen));
201
    }
202
203
    /**
204
     * @return ProjectDescriptor|null
205
     * @throws \Exception when AST file doesn't exist
206
     */
207
    protected function getAst()
208
    {
209
        $file = $this->environmentContext->getWorkingDir() . '/ast.dump';
210
        if (!file_exists($file)) {
211
            throw new \Exception(
212
                'The output of phpDocumentor was not generated, this probably means that the execution failed. '
213
                . 'The error output was: ' . $this->environmentContext->getErrorOutput()
214
            );
215
        }
216
217
        return unserialize(file_get_contents($file));
218
    }
219
220
    /**
221
     * @param string $tagName
222
     * @param int $expectedNumber
223
     * @param DescriptorAbstract $element
224
     */
225
    private static function AssertTagCount($element, $tagName, $expectedNumber)
0 ignored issues
show
Coding Style introduced by
Method name "ApiContext::AssertTagCount" is not in camel caps format
Loading history...
226
    {
227
        /** @var Collection $tagCollection */
228
        $tagCollection = $element->getTags()->get($tagName, new Collection());
229
230
        Assert::assertEquals((int)$expectedNumber, $tagCollection->count());
231
        if ($expectedNumber > 0) {
232
            Assert::assertEquals($tagName, $tagCollection[0]->getName());
233
        }
234
    }
235
}
236