Mercurial > hg > isophonics-drupal-site
comparison vendor/phpunit/php-code-coverage/src/Node/File.php @ 14:1fec387a4317
Update Drupal core to 8.5.2 via Composer
author | Chris Cannam |
---|---|
date | Mon, 23 Apr 2018 09:46:53 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
13:5fb285c0d0e3 | 14:1fec387a4317 |
---|---|
1 <?php | |
2 /* | |
3 * This file is part of the php-code-coverage package. | |
4 * | |
5 * (c) Sebastian Bergmann <sebastian@phpunit.de> | |
6 * | |
7 * For the full copyright and license information, please view the LICENSE | |
8 * file that was distributed with this source code. | |
9 */ | |
10 | |
11 namespace SebastianBergmann\CodeCoverage\Node; | |
12 | |
13 use SebastianBergmann\CodeCoverage\InvalidArgumentException; | |
14 | |
15 /** | |
16 * Represents a file in the code coverage information tree. | |
17 */ | |
18 class File extends AbstractNode | |
19 { | |
20 /** | |
21 * @var array | |
22 */ | |
23 private $coverageData; | |
24 | |
25 /** | |
26 * @var array | |
27 */ | |
28 private $testData; | |
29 | |
30 /** | |
31 * @var int | |
32 */ | |
33 private $numExecutableLines = 0; | |
34 | |
35 /** | |
36 * @var int | |
37 */ | |
38 private $numExecutedLines = 0; | |
39 | |
40 /** | |
41 * @var array | |
42 */ | |
43 private $classes = []; | |
44 | |
45 /** | |
46 * @var array | |
47 */ | |
48 private $traits = []; | |
49 | |
50 /** | |
51 * @var array | |
52 */ | |
53 private $functions = []; | |
54 | |
55 /** | |
56 * @var array | |
57 */ | |
58 private $linesOfCode = []; | |
59 | |
60 /** | |
61 * @var int | |
62 */ | |
63 private $numClasses = null; | |
64 | |
65 /** | |
66 * @var int | |
67 */ | |
68 private $numTestedClasses = 0; | |
69 | |
70 /** | |
71 * @var int | |
72 */ | |
73 private $numTraits = null; | |
74 | |
75 /** | |
76 * @var int | |
77 */ | |
78 private $numTestedTraits = 0; | |
79 | |
80 /** | |
81 * @var int | |
82 */ | |
83 private $numMethods = null; | |
84 | |
85 /** | |
86 * @var int | |
87 */ | |
88 private $numTestedMethods = null; | |
89 | |
90 /** | |
91 * @var int | |
92 */ | |
93 private $numTestedFunctions = null; | |
94 | |
95 /** | |
96 * @var array | |
97 */ | |
98 private $startLines = []; | |
99 | |
100 /** | |
101 * @var array | |
102 */ | |
103 private $endLines = []; | |
104 | |
105 /** | |
106 * @var bool | |
107 */ | |
108 private $cacheTokens; | |
109 | |
110 /** | |
111 * Constructor. | |
112 * | |
113 * @param string $name | |
114 * @param AbstractNode $parent | |
115 * @param array $coverageData | |
116 * @param array $testData | |
117 * @param bool $cacheTokens | |
118 * | |
119 * @throws InvalidArgumentException | |
120 */ | |
121 public function __construct($name, AbstractNode $parent, array $coverageData, array $testData, $cacheTokens) | |
122 { | |
123 if (!\is_bool($cacheTokens)) { | |
124 throw InvalidArgumentException::create( | |
125 1, | |
126 'boolean' | |
127 ); | |
128 } | |
129 | |
130 parent::__construct($name, $parent); | |
131 | |
132 $this->coverageData = $coverageData; | |
133 $this->testData = $testData; | |
134 $this->cacheTokens = $cacheTokens; | |
135 | |
136 $this->calculateStatistics(); | |
137 } | |
138 | |
139 /** | |
140 * Returns the number of files in/under this node. | |
141 * | |
142 * @return int | |
143 */ | |
144 public function count() | |
145 { | |
146 return 1; | |
147 } | |
148 | |
149 /** | |
150 * Returns the code coverage data of this node. | |
151 * | |
152 * @return array | |
153 */ | |
154 public function getCoverageData() | |
155 { | |
156 return $this->coverageData; | |
157 } | |
158 | |
159 /** | |
160 * Returns the test data of this node. | |
161 * | |
162 * @return array | |
163 */ | |
164 public function getTestData() | |
165 { | |
166 return $this->testData; | |
167 } | |
168 | |
169 /** | |
170 * Returns the classes of this node. | |
171 * | |
172 * @return array | |
173 */ | |
174 public function getClasses() | |
175 { | |
176 return $this->classes; | |
177 } | |
178 | |
179 /** | |
180 * Returns the traits of this node. | |
181 * | |
182 * @return array | |
183 */ | |
184 public function getTraits() | |
185 { | |
186 return $this->traits; | |
187 } | |
188 | |
189 /** | |
190 * Returns the functions of this node. | |
191 * | |
192 * @return array | |
193 */ | |
194 public function getFunctions() | |
195 { | |
196 return $this->functions; | |
197 } | |
198 | |
199 /** | |
200 * Returns the LOC/CLOC/NCLOC of this node. | |
201 * | |
202 * @return array | |
203 */ | |
204 public function getLinesOfCode() | |
205 { | |
206 return $this->linesOfCode; | |
207 } | |
208 | |
209 /** | |
210 * Returns the number of executable lines. | |
211 * | |
212 * @return int | |
213 */ | |
214 public function getNumExecutableLines() | |
215 { | |
216 return $this->numExecutableLines; | |
217 } | |
218 | |
219 /** | |
220 * Returns the number of executed lines. | |
221 * | |
222 * @return int | |
223 */ | |
224 public function getNumExecutedLines() | |
225 { | |
226 return $this->numExecutedLines; | |
227 } | |
228 | |
229 /** | |
230 * Returns the number of classes. | |
231 * | |
232 * @return int | |
233 */ | |
234 public function getNumClasses() | |
235 { | |
236 if ($this->numClasses === null) { | |
237 $this->numClasses = 0; | |
238 | |
239 foreach ($this->classes as $class) { | |
240 foreach ($class['methods'] as $method) { | |
241 if ($method['executableLines'] > 0) { | |
242 $this->numClasses++; | |
243 | |
244 continue 2; | |
245 } | |
246 } | |
247 } | |
248 } | |
249 | |
250 return $this->numClasses; | |
251 } | |
252 | |
253 /** | |
254 * Returns the number of tested classes. | |
255 * | |
256 * @return int | |
257 */ | |
258 public function getNumTestedClasses() | |
259 { | |
260 return $this->numTestedClasses; | |
261 } | |
262 | |
263 /** | |
264 * Returns the number of traits. | |
265 * | |
266 * @return int | |
267 */ | |
268 public function getNumTraits() | |
269 { | |
270 if ($this->numTraits === null) { | |
271 $this->numTraits = 0; | |
272 | |
273 foreach ($this->traits as $trait) { | |
274 foreach ($trait['methods'] as $method) { | |
275 if ($method['executableLines'] > 0) { | |
276 $this->numTraits++; | |
277 | |
278 continue 2; | |
279 } | |
280 } | |
281 } | |
282 } | |
283 | |
284 return $this->numTraits; | |
285 } | |
286 | |
287 /** | |
288 * Returns the number of tested traits. | |
289 * | |
290 * @return int | |
291 */ | |
292 public function getNumTestedTraits() | |
293 { | |
294 return $this->numTestedTraits; | |
295 } | |
296 | |
297 /** | |
298 * Returns the number of methods. | |
299 * | |
300 * @return int | |
301 */ | |
302 public function getNumMethods() | |
303 { | |
304 if ($this->numMethods === null) { | |
305 $this->numMethods = 0; | |
306 | |
307 foreach ($this->classes as $class) { | |
308 foreach ($class['methods'] as $method) { | |
309 if ($method['executableLines'] > 0) { | |
310 $this->numMethods++; | |
311 } | |
312 } | |
313 } | |
314 | |
315 foreach ($this->traits as $trait) { | |
316 foreach ($trait['methods'] as $method) { | |
317 if ($method['executableLines'] > 0) { | |
318 $this->numMethods++; | |
319 } | |
320 } | |
321 } | |
322 } | |
323 | |
324 return $this->numMethods; | |
325 } | |
326 | |
327 /** | |
328 * Returns the number of tested methods. | |
329 * | |
330 * @return int | |
331 */ | |
332 public function getNumTestedMethods() | |
333 { | |
334 if ($this->numTestedMethods === null) { | |
335 $this->numTestedMethods = 0; | |
336 | |
337 foreach ($this->classes as $class) { | |
338 foreach ($class['methods'] as $method) { | |
339 if ($method['executableLines'] > 0 && | |
340 $method['coverage'] == 100) { | |
341 $this->numTestedMethods++; | |
342 } | |
343 } | |
344 } | |
345 | |
346 foreach ($this->traits as $trait) { | |
347 foreach ($trait['methods'] as $method) { | |
348 if ($method['executableLines'] > 0 && | |
349 $method['coverage'] == 100) { | |
350 $this->numTestedMethods++; | |
351 } | |
352 } | |
353 } | |
354 } | |
355 | |
356 return $this->numTestedMethods; | |
357 } | |
358 | |
359 /** | |
360 * Returns the number of functions. | |
361 * | |
362 * @return int | |
363 */ | |
364 public function getNumFunctions() | |
365 { | |
366 return \count($this->functions); | |
367 } | |
368 | |
369 /** | |
370 * Returns the number of tested functions. | |
371 * | |
372 * @return int | |
373 */ | |
374 public function getNumTestedFunctions() | |
375 { | |
376 if ($this->numTestedFunctions === null) { | |
377 $this->numTestedFunctions = 0; | |
378 | |
379 foreach ($this->functions as $function) { | |
380 if ($function['executableLines'] > 0 && | |
381 $function['coverage'] == 100) { | |
382 $this->numTestedFunctions++; | |
383 } | |
384 } | |
385 } | |
386 | |
387 return $this->numTestedFunctions; | |
388 } | |
389 | |
390 /** | |
391 * Calculates coverage statistics for the file. | |
392 */ | |
393 protected function calculateStatistics() | |
394 { | |
395 $classStack = $functionStack = []; | |
396 | |
397 if ($this->cacheTokens) { | |
398 $tokens = \PHP_Token_Stream_CachingFactory::get($this->getPath()); | |
399 } else { | |
400 $tokens = new \PHP_Token_Stream($this->getPath()); | |
401 } | |
402 | |
403 $this->processClasses($tokens); | |
404 $this->processTraits($tokens); | |
405 $this->processFunctions($tokens); | |
406 $this->linesOfCode = $tokens->getLinesOfCode(); | |
407 unset($tokens); | |
408 | |
409 for ($lineNumber = 1; $lineNumber <= $this->linesOfCode['loc']; $lineNumber++) { | |
410 if (isset($this->startLines[$lineNumber])) { | |
411 // Start line of a class. | |
412 if (isset($this->startLines[$lineNumber]['className'])) { | |
413 if (isset($currentClass)) { | |
414 $classStack[] = &$currentClass; | |
415 } | |
416 | |
417 $currentClass = &$this->startLines[$lineNumber]; | |
418 } // Start line of a trait. | |
419 elseif (isset($this->startLines[$lineNumber]['traitName'])) { | |
420 $currentTrait = &$this->startLines[$lineNumber]; | |
421 } // Start line of a method. | |
422 elseif (isset($this->startLines[$lineNumber]['methodName'])) { | |
423 $currentMethod = &$this->startLines[$lineNumber]; | |
424 } // Start line of a function. | |
425 elseif (isset($this->startLines[$lineNumber]['functionName'])) { | |
426 if (isset($currentFunction)) { | |
427 $functionStack[] = &$currentFunction; | |
428 } | |
429 | |
430 $currentFunction = &$this->startLines[$lineNumber]; | |
431 } | |
432 } | |
433 | |
434 if (isset($this->coverageData[$lineNumber])) { | |
435 if (isset($currentClass)) { | |
436 $currentClass['executableLines']++; | |
437 } | |
438 | |
439 if (isset($currentTrait)) { | |
440 $currentTrait['executableLines']++; | |
441 } | |
442 | |
443 if (isset($currentMethod)) { | |
444 $currentMethod['executableLines']++; | |
445 } | |
446 | |
447 if (isset($currentFunction)) { | |
448 $currentFunction['executableLines']++; | |
449 } | |
450 | |
451 $this->numExecutableLines++; | |
452 | |
453 if (\count($this->coverageData[$lineNumber]) > 0) { | |
454 if (isset($currentClass)) { | |
455 $currentClass['executedLines']++; | |
456 } | |
457 | |
458 if (isset($currentTrait)) { | |
459 $currentTrait['executedLines']++; | |
460 } | |
461 | |
462 if (isset($currentMethod)) { | |
463 $currentMethod['executedLines']++; | |
464 } | |
465 | |
466 if (isset($currentFunction)) { | |
467 $currentFunction['executedLines']++; | |
468 } | |
469 | |
470 $this->numExecutedLines++; | |
471 } | |
472 } | |
473 | |
474 if (isset($this->endLines[$lineNumber])) { | |
475 // End line of a class. | |
476 if (isset($this->endLines[$lineNumber]['className'])) { | |
477 unset($currentClass); | |
478 | |
479 if ($classStack) { | |
480 \end($classStack); | |
481 $key = \key($classStack); | |
482 $currentClass = &$classStack[$key]; | |
483 unset($classStack[$key]); | |
484 } | |
485 } // End line of a trait. | |
486 elseif (isset($this->endLines[$lineNumber]['traitName'])) { | |
487 unset($currentTrait); | |
488 } // End line of a method. | |
489 elseif (isset($this->endLines[$lineNumber]['methodName'])) { | |
490 unset($currentMethod); | |
491 } // End line of a function. | |
492 elseif (isset($this->endLines[$lineNumber]['functionName'])) { | |
493 unset($currentFunction); | |
494 | |
495 if ($functionStack) { | |
496 \end($functionStack); | |
497 $key = \key($functionStack); | |
498 $currentFunction = &$functionStack[$key]; | |
499 unset($functionStack[$key]); | |
500 } | |
501 } | |
502 } | |
503 } | |
504 | |
505 foreach ($this->traits as &$trait) { | |
506 foreach ($trait['methods'] as &$method) { | |
507 if ($method['executableLines'] > 0) { | |
508 $method['coverage'] = ($method['executedLines'] / | |
509 $method['executableLines']) * 100; | |
510 } else { | |
511 $method['coverage'] = 100; | |
512 } | |
513 | |
514 $method['crap'] = $this->crap( | |
515 $method['ccn'], | |
516 $method['coverage'] | |
517 ); | |
518 | |
519 $trait['ccn'] += $method['ccn']; | |
520 } | |
521 | |
522 if ($trait['executableLines'] > 0) { | |
523 $trait['coverage'] = ($trait['executedLines'] / | |
524 $trait['executableLines']) * 100; | |
525 | |
526 if ($trait['coverage'] == 100) { | |
527 $this->numTestedClasses++; | |
528 } | |
529 } else { | |
530 $trait['coverage'] = 100; | |
531 } | |
532 | |
533 $trait['crap'] = $this->crap( | |
534 $trait['ccn'], | |
535 $trait['coverage'] | |
536 ); | |
537 } | |
538 | |
539 foreach ($this->classes as &$class) { | |
540 foreach ($class['methods'] as &$method) { | |
541 if ($method['executableLines'] > 0) { | |
542 $method['coverage'] = ($method['executedLines'] / | |
543 $method['executableLines']) * 100; | |
544 } else { | |
545 $method['coverage'] = 100; | |
546 } | |
547 | |
548 $method['crap'] = $this->crap( | |
549 $method['ccn'], | |
550 $method['coverage'] | |
551 ); | |
552 | |
553 $class['ccn'] += $method['ccn']; | |
554 } | |
555 | |
556 if ($class['executableLines'] > 0) { | |
557 $class['coverage'] = ($class['executedLines'] / | |
558 $class['executableLines']) * 100; | |
559 | |
560 if ($class['coverage'] == 100) { | |
561 $this->numTestedClasses++; | |
562 } | |
563 } else { | |
564 $class['coverage'] = 100; | |
565 } | |
566 | |
567 $class['crap'] = $this->crap( | |
568 $class['ccn'], | |
569 $class['coverage'] | |
570 ); | |
571 } | |
572 | |
573 foreach ($this->functions as &$function) { | |
574 if ($function['executableLines'] > 0) { | |
575 $function['coverage'] = ($function['executedLines'] / | |
576 $function['executableLines']) * 100; | |
577 } else { | |
578 $function['coverage'] = 100; | |
579 } | |
580 | |
581 if ($function['coverage'] == 100) { | |
582 $this->numTestedFunctions++; | |
583 } | |
584 | |
585 $function['crap'] = $this->crap( | |
586 $function['ccn'], | |
587 $function['coverage'] | |
588 ); | |
589 } | |
590 } | |
591 | |
592 /** | |
593 * @param \PHP_Token_Stream $tokens | |
594 */ | |
595 protected function processClasses(\PHP_Token_Stream $tokens) | |
596 { | |
597 $classes = $tokens->getClasses(); | |
598 unset($tokens); | |
599 | |
600 $link = $this->getId() . '.html#'; | |
601 | |
602 foreach ($classes as $className => $class) { | |
603 if (!empty($class['package']['namespace'])) { | |
604 $className = $class['package']['namespace'] . '\\' . $className; | |
605 } | |
606 | |
607 $this->classes[$className] = [ | |
608 'className' => $className, | |
609 'methods' => [], | |
610 'startLine' => $class['startLine'], | |
611 'executableLines' => 0, | |
612 'executedLines' => 0, | |
613 'ccn' => 0, | |
614 'coverage' => 0, | |
615 'crap' => 0, | |
616 'package' => $class['package'], | |
617 'link' => $link . $class['startLine'] | |
618 ]; | |
619 | |
620 $this->startLines[$class['startLine']] = &$this->classes[$className]; | |
621 $this->endLines[$class['endLine']] = &$this->classes[$className]; | |
622 | |
623 foreach ($class['methods'] as $methodName => $method) { | |
624 $this->classes[$className]['methods'][$methodName] = $this->newMethod($methodName, $method, $link); | |
625 | |
626 $this->startLines[$method['startLine']] = &$this->classes[$className]['methods'][$methodName]; | |
627 $this->endLines[$method['endLine']] = &$this->classes[$className]['methods'][$methodName]; | |
628 } | |
629 } | |
630 } | |
631 | |
632 /** | |
633 * @param \PHP_Token_Stream $tokens | |
634 */ | |
635 protected function processTraits(\PHP_Token_Stream $tokens) | |
636 { | |
637 $traits = $tokens->getTraits(); | |
638 unset($tokens); | |
639 | |
640 $link = $this->getId() . '.html#'; | |
641 | |
642 foreach ($traits as $traitName => $trait) { | |
643 $this->traits[$traitName] = [ | |
644 'traitName' => $traitName, | |
645 'methods' => [], | |
646 'startLine' => $trait['startLine'], | |
647 'executableLines' => 0, | |
648 'executedLines' => 0, | |
649 'ccn' => 0, | |
650 'coverage' => 0, | |
651 'crap' => 0, | |
652 'package' => $trait['package'], | |
653 'link' => $link . $trait['startLine'] | |
654 ]; | |
655 | |
656 $this->startLines[$trait['startLine']] = &$this->traits[$traitName]; | |
657 $this->endLines[$trait['endLine']] = &$this->traits[$traitName]; | |
658 | |
659 foreach ($trait['methods'] as $methodName => $method) { | |
660 $this->traits[$traitName]['methods'][$methodName] = $this->newMethod($methodName, $method, $link); | |
661 | |
662 $this->startLines[$method['startLine']] = &$this->traits[$traitName]['methods'][$methodName]; | |
663 $this->endLines[$method['endLine']] = &$this->traits[$traitName]['methods'][$methodName]; | |
664 } | |
665 } | |
666 } | |
667 | |
668 /** | |
669 * @param \PHP_Token_Stream $tokens | |
670 */ | |
671 protected function processFunctions(\PHP_Token_Stream $tokens) | |
672 { | |
673 $functions = $tokens->getFunctions(); | |
674 unset($tokens); | |
675 | |
676 $link = $this->getId() . '.html#'; | |
677 | |
678 foreach ($functions as $functionName => $function) { | |
679 $this->functions[$functionName] = [ | |
680 'functionName' => $functionName, | |
681 'signature' => $function['signature'], | |
682 'startLine' => $function['startLine'], | |
683 'executableLines' => 0, | |
684 'executedLines' => 0, | |
685 'ccn' => $function['ccn'], | |
686 'coverage' => 0, | |
687 'crap' => 0, | |
688 'link' => $link . $function['startLine'] | |
689 ]; | |
690 | |
691 $this->startLines[$function['startLine']] = &$this->functions[$functionName]; | |
692 $this->endLines[$function['endLine']] = &$this->functions[$functionName]; | |
693 } | |
694 } | |
695 | |
696 /** | |
697 * Calculates the Change Risk Anti-Patterns (CRAP) index for a unit of code | |
698 * based on its cyclomatic complexity and percentage of code coverage. | |
699 * | |
700 * @param int $ccn | |
701 * @param float $coverage | |
702 * | |
703 * @return string | |
704 */ | |
705 protected function crap($ccn, $coverage) | |
706 { | |
707 if ($coverage == 0) { | |
708 return (string) (\pow($ccn, 2) + $ccn); | |
709 } | |
710 | |
711 if ($coverage >= 95) { | |
712 return (string) $ccn; | |
713 } | |
714 | |
715 return \sprintf( | |
716 '%01.2F', | |
717 \pow($ccn, 2) * \pow(1 - $coverage / 100, 3) + $ccn | |
718 ); | |
719 } | |
720 | |
721 /** | |
722 * @param string $methodName | |
723 * @param array $method | |
724 * @param string $link | |
725 * | |
726 * @return array | |
727 */ | |
728 private function newMethod($methodName, array $method, $link) | |
729 { | |
730 return [ | |
731 'methodName' => $methodName, | |
732 'visibility' => $method['visibility'], | |
733 'signature' => $method['signature'], | |
734 'startLine' => $method['startLine'], | |
735 'endLine' => $method['endLine'], | |
736 'executableLines' => 0, | |
737 'executedLines' => 0, | |
738 'ccn' => $method['ccn'], | |
739 'coverage' => 0, | |
740 'crap' => 0, | |
741 'link' => $link . $method['startLine'], | |
742 ]; | |
743 } | |
744 } |