comparison vendor/phpunit/php-code-coverage/src/CodeCoverage/Report/Node/File.php @ 0:4c8ae668cc8c

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