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) |
|
|
|
|
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() |
|
|
|
|
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() |
|
|
|
|
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) |
|
|
|
|
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) |
|
|
|
|
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() |
|
|
|
|
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() |
|
|
|
|
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() |
|
|
|
|
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() |
|
|
|
|
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() |
|
|
|
|
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() |
|
|
|
|
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) |
|
|
|
|
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 |
|
|
|
|
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 = '') |
|
|
|
|
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); |
|
|
|
|
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 = '') |
|
|
|
|
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); |
|
|
|
|
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) |
|
|
|
|
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) |
|
|
|
|
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) |
|
|
|
|
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) |
|
|
|
|
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() |
|
|
|
|
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
|
|
|
|
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.