Completed
Push — master ( c89193...79c6c8 )
by Kyle
11:15 queued 10s
created

AbstractTest::initVersionCompatibility()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 4
nop 0
dl 0
loc 10
rs 9.9332
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file is part of PDepend.
4
 *
5
 * PHP Version 5
6
 *
7
 * Copyright (c) 2008-2017 Manuel Pichler <[email protected]>.
8
 * All rights reserved.
9
 *
10
 * Redistribution and use in source and binary forms, with or without
11
 * modification, are permitted provided that the following conditions
12
 * are met:
13
 *
14
 *   * Redistributions of source code must retain the above copyright
15
 *     notice, this list of conditions and the following disclaimer.
16
 *
17
 *   * Redistributions in binary form must reproduce the above copyright
18
 *     notice, this list of conditions and the following disclaimer in
19
 *     the documentation and/or other materials provided with the
20
 *     distribution.
21
 *
22
 *   * Neither the name of Manuel Pichler nor the names of his
23
 *     contributors may be used to endorse or promote products derived
24
 *     from this software without specific prior written permission.
25
 *
26
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
28
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
29
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
30
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
31
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
32
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
33
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
34
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
36
 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37
 * POSSIBILITY OF SUCH DAMAGE.
38
 *
39
 * @copyright 2008-2017 Manuel Pichler. All rights reserved.
40
 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
41
  */
42
43
namespace PDepend;
44
45
use PDepend\Input\ExcludePathFilter;
46
use PDepend\Input\Iterator;
47
use PDepend\Source\AST\ASTArtifactList\CollectionArtifactFilter;
48
use PDepend\Source\AST\ASTClass;
49
use PDepend\Source\AST\ASTCompilationUnit;
50
use PDepend\Source\AST\ASTFormalParameters;
51
use PDepend\Source\AST\ASTFunction;
52
use PDepend\Source\AST\ASTInterface;
53
use PDepend\Source\AST\ASTMethod;
54
use PDepend\Source\AST\ASTNode;
55
use PDepend\Source\AST\ASTTrait;
56
use PDepend\Source\Builder\Builder;
57
use PDepend\Source\Builder\BuilderContext;
58
use PDepend\Source\Language\PHP\PHPBuilder;
59
use PDepend\Source\Language\PHP\PHPParserGeneric;
60
use PDepend\Source\Language\PHP\PHPTokenizerInternal;
61
use PDepend\Source\Tokenizer\Tokenizer;
62
use PDepend\Util\Cache\CacheDriver;
63
use PDepend\Util\Cache\Driver\MemoryCacheDriver;
64
65
/**
66
 * Abstract test case implementation for the PDepend namespace.
67
 *
68
 * @copyright 2008-2017 Manuel Pichler. All rights reserved.
69
 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
70
 */
71
abstract class AbstractTest extends \PHPUnit_Framework_TestCase
72
{
73
    /**
74
     * The current working directory.
75
     *
76
     * @var string
77
     * @since 0.10.0
78
     */
79
    protected $workingDirectory = null;
80
81
    /**
82
     * Removes temporary test contents.
83
     *
84
     * @return void
85
     */
86
    protected function setUp()
87
    {
88
        parent::setUp();
89
90
        $run = __DIR__ . '/_run';
91
        if (file_exists($run) === false) {
92
            mkdir($run, 0755);
93
        }
94
95
        $this->clearRunResources($run);
96
97
        if (defined('STDERR') === false) {
98
            define('STDERR', fopen('php://stderr', true));
99
        }
100
    }
101
102
    /**
103
     * Resets the global iterator filter.
104
     *
105
     * @return void
106
     */
107
    protected function tearDown()
108
    {
109
        CollectionArtifactFilter::getInstance()->setFilter();
110
111
        $this->clearRunResources();
112
        $this->resetWorkingDirectory();
113
114
        if (function_exists('gc_collect_cycles')) {
115
            gc_collect_cycles();
116
        }
117
118
        parent::tearDown();
119
    }
120
121
    /**
122
     * Changes the current working directory to an directory associated with the
123
     * calling test case.
124
     *
125
     * @param string $directory Optional working directory.
126
     *
127
     * @return void
128
     * @since 0.10.0
129
     */
130
    protected function changeWorkingDirectory($directory = null)
131
    {
132
        if (null === $directory) {
133
            $directory = $this->getTestWorkingDirectory();
134
        }
135
136
        $this->workingDirectory = getcwd();
137
        chdir($directory);
138
    }
139
140
    /**
141
     * Returns the working directory for the currently executed test.
142
     *
143
     * @return string
144
     * @since 1.0.0
145
     */
146
    protected function getTestWorkingDirectory()
147
    {
148
        $resource = $this->createCodeResourceUriForTest();
149
        if (is_file($resource)) {
150
            return dirname($resource);
151
        }
152
        return $resource;
153
    }
154
155
    /**
156
     * Resets a previous changed working directory.
157
     *
158
     * @return void
159
     * @since 0.10.0
160
     */
161
    protected function resetWorkingDirectory()
162
    {
163
        if ($this->workingDirectory) {
164
            chdir($this->workingDirectory);
165
        }
166
        $this->workingDirectory = null;
167
    }
168
169
    /**
170
     * Returns a node instance for the currently executed test case.
171
     *
172
     * @param string $testCase Name of the calling test case.
173
     * @param string $nodeType The searched node class.
174
     *
175
     * @return \PDepend\Source\AST\ASTNode
176
     */
177
    protected function getFirstNodeOfTypeInFunction($testCase, $nodeType)
178
    {
179
        return $this->getFirstFunctionForTestCase($testCase)
0 ignored issues
show
Unused Code introduced by
The call to AbstractTest::getFirstFunctionForTestCase() has too many arguments starting with $testCase.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
180
            ->getFirstChildOfType($nodeType);
181
    }
182
183
    /**
184
     * Returns the first function found in a test file associated with the
185
     * given test case.
186
     *
187
     * @return ASTFunction
188
     */
189
    protected function getFirstFunctionForTestCase()
190
    {
191
        return $this->parseCodeResourceForTest()
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface PDepend\Source\AST\ASTArtifact as the method getFunctions() does only exist in the following implementations of said interface: PDepend\Source\AST\ASTNamespace.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
192
            ->current()
193
            ->getFunctions()
194
            ->current();
195
    }
196
197
    /**
198
     * @return \PDepend\Source\AST\ASTClosure
199
     */
200
    protected function getFirstClosureForTestCase()
201
    {
202
        return $this->parseCodeResourceForTest()
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface PDepend\Source\AST\ASTArtifact as the method getFunctions() does only exist in the following implementations of said interface: PDepend\Source\AST\ASTNamespace.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
203
            ->current()
204
            ->getFunctions()
205
            ->current()
206
            ->getFirstChildOfType('PDepend\\Source\\AST\\ASTClosure');
207
    }
208
209
    /**
210
     * Create a TextUi Runner
211
     *
212
     * @return Runner
213
     */
214
    protected function createTextUiRunner()
215
    {
216
        $application = $this->createTestApplication();
217
218
        return $application->getRunner();
219
    }
220
221
    protected function createTestApplication()
222
    {
223
        $application = new \PDepend\Application();
224
        $application->setConfigurationFile(__DIR__ . '/../../resources/pdepend.xml.dist');
225
226
        return $application;
227
    }
228
229
    /**
230
     * Returns a node instance for the currently executed test case.
231
     *
232
     * @param string $nodeType The searched node class.
233
     *
234
     * @return \PDepend\Source\AST\ASTNode
235
     * @since 1.0.0
236
     */
237
    protected function getFirstNodeOfTypeInTrait($nodeType)
238
    {
239
        return $this->getFirstTraitForTestCase()
240
            ->getFirstChildOfType($nodeType);
241
    }
242
243
    /**
244
     * Returns a node instance for the currently executed test case.
245
     *
246
     * @param string $testCase Name of the calling test case.
247
     * @param string $nodeType The searched node class.
248
     *
249
     * @return \PDepend\Source\AST\ASTNode
250
     */
251
    protected function getFirstNodeOfTypeInClass($testCase, $nodeType)
252
    {
253
        return $this->getFirstClassForTestCase($testCase)
0 ignored issues
show
Unused Code introduced by
The call to AbstractTest::getFirstClassForTestCase() has too many arguments starting with $testCase.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
254
            ->getFirstChildOfType($nodeType);
255
    }
256
257
    /**
258
     * Returns a node instance for the currently executed test case.
259
     *
260
     * @param string $testCase Name of the calling test case.
261
     * @param string $nodeType The searched node class.
262
     *
263
     * @return \PDepend\Source\AST\ASTNode
264
     */
265
    protected function getFirstNodeOfTypeInInterface($testCase, $nodeType)
266
    {
267
        return $this->getFirstInterfaceForTestCase($testCase)
0 ignored issues
show
Unused Code introduced by
The call to AbstractTest::getFirstInterfaceForTestCase() has too many arguments starting with $testCase.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
268
            ->getFirstChildOfType($nodeType);
269
    }
270
271
    /**
272
     * Returns the first trait found in a test file associated with the given
273
     * test case.
274
     *
275
     * @return \PDepend\Source\AST\ASTTrait
276
     * @since 1.0.0
277
     */
278
    protected function getFirstTraitForTestCase()
279
    {
280
        return $this->parseCodeResourceForTest()
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface PDepend\Source\AST\ASTArtifact as the method getTraits() does only exist in the following implementations of said interface: PDepend\Source\AST\ASTNamespace.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
281
            ->current()
282
            ->getTraits()
283
            ->current();
284
    }
285
286
    /**
287
     * Returns the first class found in a test file associated with the given
288
     * test case.
289
     *
290
     * @return \PDepend\Source\AST\ASTClass
291
     */
292
    protected function getFirstClassForTestCase()
293
    {
294
        return $this->parseCodeResourceForTest()
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface PDepend\Source\AST\ASTArtifact as the method getClasses() does only exist in the following implementations of said interface: PDepend\Source\AST\ASTNamespace.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
295
            ->current()
296
            ->getClasses()
297
            ->current();
298
    }
299
300
    /**
301
     * Returns the first method that could be found in the source file
302
     * associated with the calling test case.
303
     *
304
     * @return \PDepend\Source\AST\ASTMethod
305
     */
306
    protected function getFirstClassMethodForTestCase()
307
    {
308
        return $this->parseCodeResourceForTest()
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface PDepend\Source\AST\ASTArtifact as the method getClasses() does only exist in the following implementations of said interface: PDepend\Source\AST\ASTNamespace.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
309
            ->current()
310
            ->getClasses()
311
            ->current()
312
            ->getMethods()
313
            ->current();
314
    }
315
316
    /**
317
     * Returns the first interface that could be found in the source file
318
     * associated with the calling test case.
319
     *
320
     * @return \PDepend\Source\AST\ASTInterface
321
     */
322
    protected function getFirstInterfaceForTestCase()
323
    {
324
        return $this->parseCodeResourceForTest()
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface PDepend\Source\AST\ASTArtifact as the method getInterfaces() does only exist in the following implementations of said interface: PDepend\Source\AST\ASTAnonymousClass, PDepend\Source\AST\ASTClass, PDepend\Source\AST\ASTInterface, PDepend\Source\AST\ASTNamespace, PDepend\Source\AST\ASTTrait, PDepend\Source\AST\AbstractASTClassOrInterface.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
325
            ->current()
326
            ->getInterfaces()
327
            ->current();
328
    }
329
330
    /**
331
     * Returns the first method that could be found in the source file
332
     * associated with the calling test case.
333
     *
334
     * @return \PDepend\Source\AST\ASTMethod
335
     */
336
    protected function getFirstInterfaceMethodForTestCase()
337
    {
338
        return $this->parseCodeResourceForTest()
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface PDepend\Source\AST\ASTArtifact as the method getInterfaces() does only exist in the following implementations of said interface: PDepend\Source\AST\ASTAnonymousClass, PDepend\Source\AST\ASTClass, PDepend\Source\AST\ASTInterface, PDepend\Source\AST\ASTNamespace, PDepend\Source\AST\ASTTrait, PDepend\Source\AST\AbstractASTClassOrInterface.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
339
            ->current()
340
            ->getInterfaces()
341
            ->current()
342
            ->getMethods()
343
            ->current();
344
    }
345
346
    /**
347
     * Returns the first class or interface that could be found in the code under
348
     * test for the calling test case.
349
     *
350
     * @return \PDepend\Source\AST\AbstractASTClassOrInterface
351
     */
352
    protected function getFirstTypeForTestCase()
353
    {
354
        return $this->parseCodeResourceForTest()
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface PDepend\Source\AST\ASTArtifact as the method getTypes() does only exist in the following implementations of said interface: PDepend\Source\AST\ASTNamespace.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
355
            ->current()
356
            ->getTypes()
357
            ->current();
358
    }
359
360
    /**
361
     * Returns the first method that could be found in the code under test for
362
     * the calling test case.
363
     *
364
     * @return \PDepend\Source\AST\ASTMethod
365
     */
366
    protected function getFirstMethodForTestCase()
367
    {
368
        return $this->getFirstTypeForTestCase()
369
            ->getMethods()
370
            ->current();
371
    }
372
373
    /**
374
     * @return \PDepend\Source\AST\ASTFormalParameter
375
     */
376
    protected function getFirstFormalParameterForTestCase()
377
    {
378
        return $this->getFirstFunctionForTestCase()
379
            ->getFirstChildOfType(
380
                'PDepend\\Source\\AST\\ASTFormalParameter'
381
            );
382
    }
383
384
    /**
385
     * Collects all children from a given node.
386
     *
387
     * @param \PDepend\Source\AST\ASTNode $node   The current root node.
388
     * @param array                   $actual Previous filled list.
389
     *
390
     * @return array(string)
0 ignored issues
show
Documentation introduced by
The doc-type array(string) could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
391
     */
392
    protected static function collectChildNodes(ASTNode $node, array $actual = array())
393
    {
394
        foreach ($node->getChildren() as $child) {
395
            $actual[] = get_class($child);
396
            $actual   = self::collectChildNodes($child, $actual);
397
        }
398
        return $actual;
399
    }
400
401
    /**
402
     * Tests that the given node and its children represent the expected ast
403
     * object graph.
404
     *
405
     * @param \PDepend\Source\AST\ASTNode $node
406
     * @param array(string) $expected
0 ignored issues
show
Documentation introduced by
The doc-type array(string) could not be parsed: Expected "|" or "end of type", but got "(" at position 5. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
407
     *
408
     * @return void
409
     */
410
    protected static function assertGraphEquals(ASTNode $node, array $expected)
411
    {
412
        self::assertEquals($expected, self::collectChildNodes($node));
413
    }
414
415
    /**
416
     * Collects all children from a given node.
417
     *
418
     * @param \PDepend\Source\AST\ASTNode $node The current root node.
419
     *
420
     * @return array
421
     */
422
    protected static function collectGraph(ASTNode $node)
423
    {
424
        $graph = array();
425
        foreach ($node->getChildren() as $child) {
426
            $graph[] = get_class($child) . ' (' . $child->getImage() . ')';
427
            if (0 < count($child->getChildren())) {
428
                $graph[] = self::collectGraph($child);
429
            }
430
        }
431
        return $graph;
432
    }
433
434
    /**
435
     * Tests that the given node and its children represent the expected ast
436
     * object graph.
437
     *
438
     * @param \PDepend\Source\AST\ASTNode $node  The root node.
439
     * @param array                   $graph Expected class structure.
440
     *
441
     * @return void
442
     */
443
    protected static function assertGraph(ASTNode $node, $graph)
444
    {
445
        $actual = self::collectGraph($node);
446
        self::assertEquals($graph, $actual);
447
    }
448
449
    /**
450
     * Helper method to allow PHPUnit versions < 3.5.x
451
     *
452
     * @param string $expected The expected class or interface.
453
     * @param mixed  $actual   The actual variable/value.
454
     * @param string $message  Optional error/fail message.
455
     *
456
     * @return void
457
     * @since 0.10.2
458
     */
459 View Code Duplication
    public static function assertInstanceOf($expected, $actual, $message = '')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
460
    {
461
        if (is_callable(get_parent_class(__CLASS__) . '::') . __FUNCTION__) {
462
            return parent::assertInstanceOf($expected, $actual, $message);
463
        }
464
        return parent::assertType($expected, $actual, $message);
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (assertType() instead of assertInstanceOf()). Are you sure this is correct? If so, you might want to change this to $this->assertType().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
Bug introduced by
The method assertType() does not seem to exist on object<PHPUnit_Framework_TestCase>.

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...
465
    }
466
467
    /**
468
     * Helper method to allow PHPUnit versions < 3.5.x
469
     *
470
     * @param string $expected The expected internal type.
471
     * @param mixed  $actual   The actual variable/value.
472
     * @param string $message  Optional error/fail message.
473
     *
474
     * @return void
475
     * @since 0.10.2
476
     */
477 View Code Duplication
    public static function assertInternalType($expected, $actual, $message = '')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
478
    {
479
        if (is_callable(get_parent_class(__CLASS__) . '::') . __FUNCTION__) {
480
            return parent::assertInternalType($expected, $actual, $message);
481
        }
482
        return parent::assertType($expected, $actual, $message);
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (assertType() instead of assertInternalType()). Are you sure this is correct? If so, you might want to change this to $this->assertType().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
Bug introduced by
The method assertType() does not seem to exist on object<PHPUnit_Framework_TestCase>.

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...
483
    }
484
485
    /**
486
     * Creates a mocked class instance without calling the constructor.
487
     *
488
     * @param string $className Name of the class to mock.
489
     *
490
     * @return stdClass
491
     * @since 0.10.0
492
     */
493
    protected function getMockWithoutConstructor($className)
494
    {
495
        $mock = $this->getMockBuilder($className)
496
            ->setMethods(array('__construct'))
497
            ->disableOriginalConstructor()
498
            ->getMock();
499
500
        return $mock;
501
    }
502
503
    /**
504
     * Clears all temporary resources.
505
     *
506
     * @param string $dir
507
     * @return void
508
     */
509
    private function clearRunResources($dir = null)
510
    {
511
        if ($dir === null) {
512
            $dir = __DIR__ . '/_run';
513
        }
514
515
        foreach (new \DirectoryIterator($dir) as $file) {
516
            if ($file == '.' || $file == '..' || $file == '.svn') {
517
                continue;
518
            }
519
            $pathName = realpath($file->getPathname());
520
            if ($file->isDir()) {
521
                $this->clearRunResources($pathName);
522
                rmdir($pathName);
523
            } else {
524
                unlink($pathName);
525
            }
526
        }
527
    }
528
529
    /**
530
     * Creates a test configuration instance.
531
     *
532
     * @return \PDepend\Util\Configuration
533
     * @since 0.10.0
534
     */
535
    protected function createConfigurationFixture()
536
    {
537
        $application = $this->createTestApplication();
538
539
        return $application->getConfiguration();
540
    }
541
542
    /**
543
     * @return \PDepend\Util\Cache\CacheDriver
544
     */
545
    protected function createCacheFixture()
546
    {
547
        $cache = $this->getMockBuilder('\\PDepend\\Util\\Cache\\CacheDriver')
548
            ->getMock();
549
        
550
        return $cache;
551
    }
552
553
    /**
554
     * Creates a PDepend instance configured with the code resource associated
555
     * with the calling test case.
556
     *
557
     * @return \PDepend\Engine
558
     * @since 0.10.0
559
     */
560
    protected function createEngineFixture()
561
    {
562
        $this->changeWorkingDirectory(
563
            $this->createCodeResourceURI('config/')
564
        );
565
566
        $application = $this->createTestApplication();
567
568
        $configuration = $application->getConfiguration();
569
        $cacheFactory = new \PDepend\Util\Cache\CacheFactory($configuration);
570
        $analyzerFactory = $application->getAnalyzerFactory();
571
572
        return new Engine($configuration, $cacheFactory, $analyzerFactory);
573
    }
574
575
    protected function silentRun($runner)
576
    {
577
        ob_start();
578
        $exitCode = $runner->run();
579
        ob_end_clean();
580
581
        return $exitCode;
582
    }
583
584
    /**
585
     * Creates a ready to use class fixture.
586
     *
587
     * @param string $name Optional class name.
588
     *
589
     * @return \PDepend\Source\AST\ASTClass
590
     * @since 1.0.2
591
     */
592
    protected function createClassFixture($name = null)
593
    {
594
        $name = $name ? $name : get_class($this);
595
596
        $class = new ASTClass($name);
597
        $class->setCompilationUnit(new ASTCompilationUnit($GLOBALS['argv'][0]));
598
        $class->setCache(new MemoryCacheDriver());
599
        $context = $this->getMockBuilder('PDepend\\Source\\Builder\\BuilderContext')
600
            ->getMock();
601
        $class->setContext($context);
602
603
        return $class;
604
    }
605
606
    /**
607
     * Creates a ready to use interface fixture.
608
     *
609
     * @param string $name Optional interface name.
610
     *
611
     * @return \PDepend\Source\AST\ASTInterface
612
     * @since 1.0.2
613
     */
614 View Code Duplication
    protected function createInterfaceFixture($name = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
615
    {
616
        $name = $name ? $name : get_class($this);
617
618
        $interface = new ASTInterface($name);
619
        $interface->setCompilationUnit(new ASTCompilationUnit($GLOBALS['argv'][0]));
620
        $interface->setCache(new MemoryCacheDriver());
621
622
        return $interface;
623
    }
624
625
    /**
626
     * Creates a ready to use trait fixture.
627
     *
628
     * @param string $name Optional trait name.
629
     * @return \PDepend\Source\AST\ASTTrait
630
     * @since 1.0.2
631
     */
632
    protected function createTraitFixture($name = null)
633
    {
634
        $name = $name ? $name : get_class($this);
635
636
        $trait = new ASTTrait($name);
637
        $trait->setCache(new MemoryCacheDriver());
638
639
        return $trait;
640
    }
641
642
    /**
643
     * Creates a ready to use function fixture.
644
     *
645
     * @param string $name Optional function name.
646
     * @return \PDepend\Source\AST\ASTFunction
647
     * @since 1.0.2
648
     */
649 View Code Duplication
    protected function createFunctionFixture($name = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
650
    {
651
        $name = $name ? $name : get_class($this);
652
653
        $function = new ASTFunction($name);
654
        $function->setCompilationUnit(new ASTCompilationUnit($GLOBALS['argv'][0]));
655
        $function->setCache(new MemoryCacheDriver());
656
        $function->addChild(new ASTFormalParameters());
657
658
        return $function;
659
    }
660
661
    /**
662
     * Creates a ready to use method fixture.
663
     *
664
     * @param string $name Optional method name.
665
     * @return \PDepend\Source\AST\ASTMethod
666
     * @since 1.0.2
667
     */
668
    protected function createMethodFixture($name = null)
669
    {
670
        $name = $name ? $name : get_class($this);
671
672
        $method = new ASTMethod($name);
673
        $method->setCache(new MemoryCacheDriver());
674
        $method->addChild(new ASTFormalParameters());
675
676
        return $method;
677
    }
678
679
    /**
680
     * Creates a temporary resource for the given file name.
681
     *
682
     * @param string $fileName
683
     * @return string
684
     * @throws \ErrorException
685
     */
686 View Code Duplication
    protected function createRunResourceURI($fileName = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
687
    {
688
        $uri = __DIR__ . '/_run/' . ($fileName ? $fileName : uniqid());
689
        if (file_exists($uri) === true) {
690
            throw new \ErrorException("File '{$fileName}' already exists.");
691
        }
692
        return $uri;
693
    }
694
695
    /**
696
     * Creates a code uri for the given file name.
697
     *
698
     * @param string $fileName The code file name.
699
     * @return string
700
     * @throws \ErrorException
701
     */
702 View Code Duplication
    protected function createCodeResourceURI($fileName)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
703
    {
704
        $uri = realpath(__DIR__ . '/../../resources/files') . DIRECTORY_SEPARATOR . $fileName;
705
706
        if (file_exists($uri) === false) {
707
            throw new \ErrorException("File '{$fileName}' does not exists.");
708
        }
709
        return $uri;
710
    }
711
712
    /**
713
     * Creates a code uri for the calling test case.
714
     *
715
     * @return string
716
     */
717
    protected function createCodeResourceUriForTest()
718
    {
719
        list($class, $method) = explode('::', $this->getCallingTestMethod());
720
721
        if (1 === count($parts = explode('\\', $class))) {
722
            $parts = explode('\\', $class);
723
        }
724
725
726
        // Strip first two parts
727
        array_shift($parts);
728
729
        if (!preg_match('(Version\d+Test$)', end($parts)) && preg_match('(\D(\d+Test)$)', end($parts), $match)) {
730
            array_pop($parts);
731
            array_push($parts, $match[1]);
732
733
            // TODO: Fix this workaround for the existing lower case directories
734
            array_unshift($parts, strtolower(array_shift($parts)));
735
        }
736
737
        $fileName = substr(join(DIRECTORY_SEPARATOR, $parts), 0, -4) . DIRECTORY_SEPARATOR . $method;
738
        try {
739
            return $this->createCodeResourceURI($fileName);
740
        } catch (\ErrorException $e) {
741
            return $this->createCodeResourceURI("{$fileName}.php");
742
        }
743
    }
744
745
    /**
746
     * Returns the name of the calling test method.
747
     *
748
     * @return string
749
     */
750 View Code Duplication
    protected function getCallingTestMethod()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
751
    {
752
        foreach (debug_backtrace() as $frame) {
753
            if (strpos($frame['function'], 'test') === 0) {
754
                return "{$frame['class']}::{$frame['function']}";
755
            }
756
        }
757
        throw new \ErrorException("No calling test case found.");
758
    }
759
760
    /**
761
     * Initializes the test environment.
762
     *
763
     * @return void
764
     */
765
    public static function init()
766
    {
767
        // First register autoloader
768
        spl_autoload_register(array(__CLASS__, 'autoload'));
769
770
        // Is it not installed?
771
        if (is_file(dirname(__FILE__) . '/../../../main/php/PDepend/Engine.php')) {
772
            $path  = realpath(dirname(__FILE__) . '/../../../main/php/');
773
            $path .= PATH_SEPARATOR . get_include_path();
774
            set_include_path($path);
775
        }
776
777
        // Set test path
778
        $path  = realpath(dirname(__FILE__) . '/..');
779
        $path .= PATH_SEPARATOR . get_include_path();
780
        set_include_path($path);
781
782
        self::initVersionCompatibility();
783
    }
784
785
    /**
786
     * Autoloader for the test cases.
787
     *
788
     * @param string $className Name of the missing class.
789
     * @return void
790
     */
791
    public static function autoload($className)
792
    {
793
        $file = strtr($className, '\\', DIRECTORY_SEPARATOR) . '.php';
794
        if (is_file(dirname(__FILE__) . '/../../../main/php/PDepend/Engine.php')) {
795
            $file = dirname(__FILE__) . '/../../../main/php/' . $file;
796
        }
797
        if (file_exists($file)) {
798
            include $file;
799
        }
800
    }
801
802
    /**
803
     * There was an api change between PHP 5.3.0alpha3 and 5.3.0beta1, the new
804
     * extension name "Core" was introduced and interfaces like "Iterator" are
805
     * now part of "Core" instead of "Standard".
806
     *
807
     * @return void
808
     */
809
    private static function initVersionCompatibility()
810
    {
811
        $reflection = new \ReflectionClass('Iterator');
812
        $extension  = strtolower($reflection->getExtensionName());
813
        $extension  = ($extension === '' ? 'standard' : $extension);
814
815
        if (defined('CORE_PACKAGE') === false) {
816
            define('CORE_PACKAGE', '+' . $extension);
817
        }
818
    }
819
820
    /**
821
     * Parses the test code associated with the calling test method.
822
     *
823
     * @param boolean $ignoreAnnotations The parser should ignore annotations.
824
     * @return \PDepend\Source\AST\ASTNamespace[]
825
     */
826
    protected function parseCodeResourceForTest($ignoreAnnotations = false)
827
    {
828
        return $this->parseSource(
829
            $this->createCodeResourceUriForTest(),
830
            $ignoreAnnotations
831
        );
832
    }
833
834
    /**
835
     * Parses the given source file or directory with the default tokenizer
836
     * and node builder implementations.
837
     *
838
     * @param string $testCase
839
     * @param boolean $ignoreAnnotations
840
     * @return \PDepend\Source\AST\ASTNamespace[]
841
     */
842
    public function parseTestCaseSource($testCase, $ignoreAnnotations = false)
843
    {
844
        list($class, $method) = explode('::', $testCase);
845
846
        $fileName = substr(strtolower($class), 8, strrpos($class, '\\') - 8);
847
        $fileName = str_replace('\\', DIRECTORY_SEPARATOR, $fileName) . DIRECTORY_SEPARATOR . $method;
848
849
        try {
850
            $fileOrDirectory = $this->createCodeResourceURI($fileName);
851
        } catch (\ErrorException $e) {
852
            $fileOrDirectory = $this->createCodeResourceURI($fileName . '.php');
853
        }
854
855
        return $this->parseSource($fileOrDirectory, $ignoreAnnotations);
856
    }
857
858
    /**
859
     * Parses the given source file or directory with the default tokenizer
860
     * and node builder implementations.
861
     *
862
     * @param string  $fileOrDirectory
863
     * @param boolean $ignoreAnnotations
864
     * @return \PDepend\Source\AST\ASTNamespace[]
865
     */
866
    public function parseSource($fileOrDirectory, $ignoreAnnotations = false)
867
    {
868
        if (file_exists($fileOrDirectory) === false) {
869
            $fileOrDirectory = $this->createCodeResourceURI($fileOrDirectory);
870
        }
871
872
        if (is_dir($fileOrDirectory)) {
873
            $it = new Iterator(
874
                new \RecursiveIteratorIterator(
875
                    new \RecursiveDirectoryIterator($fileOrDirectory)
876
                ),
877
                new ExcludePathFilter(array('.svn'))
878
            );
879
        } else {
880
            $it = new \ArrayIterator(array($fileOrDirectory));
881
        }
882
883
        $files = array();
884
        foreach ($it as $file) {
885
            if (is_object($file)) {
886
                $files[] = realpath($file->getPathname());
887
            } else {
888
                $files[] = $file;
889
            }
890
        }
891
        sort($files);
892
893
        $cache   = new MemoryCacheDriver();
894
        $builder = new PHPBuilder();
895
896
        foreach ($files as $file) {
897
            $tokenizer = new PHPTokenizerInternal();
898
            $tokenizer->setSourceFile($file);
899
900
            $parser = $this->createPHPParser($tokenizer, $builder, $cache);
901
            if ($ignoreAnnotations === true) {
902
                $parser->setIgnoreAnnotations();
903
            }
904
905
            $parser->parse();
906
        }
907
        return $builder->getNamespaces();
908
    }
909
910
    /**
911
     * @param \PDepend\Source\Tokenizer\Tokenizer $tokenizer
912
     * @param \PDepend\Source\Builder\Builder $builder
913
     * @param \PDepend\Util\Cache\CacheDriver $cache
914
     * @return \PDepend\Source\Language\PHP\AbstractPHPParser
915
     */
916
    protected function createPHPParser(Tokenizer $tokenizer, Builder $builder, CacheDriver $cache)
917
    {
918
        return new PHPParserGeneric(
919
            $tokenizer,
920
            $builder,
921
            $cache
922
        );
923
    }
924
}
925
926
AbstractTest::init();
927