annotate vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/File.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 1fec387a4317
children
rev   line source
Chris@14 1 <?php
Chris@14 2 /*
Chris@14 3 * This file is part of the php-code-coverage package.
Chris@14 4 *
Chris@14 5 * (c) Sebastian Bergmann <sebastian@phpunit.de>
Chris@14 6 *
Chris@14 7 * For the full copyright and license information, please view the LICENSE
Chris@14 8 * file that was distributed with this source code.
Chris@14 9 */
Chris@14 10
Chris@14 11 namespace SebastianBergmann\CodeCoverage\Report\Html;
Chris@14 12
Chris@14 13 use SebastianBergmann\CodeCoverage\Node\File as FileNode;
Chris@14 14 use SebastianBergmann\CodeCoverage\Util;
Chris@14 15
Chris@14 16 /**
Chris@14 17 * Renders a file node.
Chris@14 18 */
Chris@14 19 class File extends Renderer
Chris@14 20 {
Chris@14 21 /**
Chris@14 22 * @var int
Chris@14 23 */
Chris@14 24 private $htmlspecialcharsFlags;
Chris@14 25
Chris@14 26 /**
Chris@14 27 * Constructor.
Chris@14 28 *
Chris@14 29 * @param string $templatePath
Chris@14 30 * @param string $generator
Chris@14 31 * @param string $date
Chris@14 32 * @param int $lowUpperBound
Chris@14 33 * @param int $highLowerBound
Chris@14 34 */
Chris@14 35 public function __construct($templatePath, $generator, $date, $lowUpperBound, $highLowerBound)
Chris@14 36 {
Chris@14 37 parent::__construct(
Chris@14 38 $templatePath,
Chris@14 39 $generator,
Chris@14 40 $date,
Chris@14 41 $lowUpperBound,
Chris@14 42 $highLowerBound
Chris@14 43 );
Chris@14 44
Chris@14 45 $this->htmlspecialcharsFlags = ENT_COMPAT;
Chris@14 46
Chris@14 47 $this->htmlspecialcharsFlags = $this->htmlspecialcharsFlags | ENT_HTML401 | ENT_SUBSTITUTE;
Chris@14 48 }
Chris@14 49
Chris@14 50 /**
Chris@14 51 * @param FileNode $node
Chris@14 52 * @param string $file
Chris@14 53 */
Chris@14 54 public function render(FileNode $node, $file)
Chris@14 55 {
Chris@14 56 $template = new \Text_Template($this->templatePath . 'file.html', '{{', '}}');
Chris@14 57
Chris@14 58 $template->setVar(
Chris@14 59 [
Chris@14 60 'items' => $this->renderItems($node),
Chris@14 61 'lines' => $this->renderSource($node)
Chris@14 62 ]
Chris@14 63 );
Chris@14 64
Chris@14 65 $this->setCommonTemplateVariables($template, $node);
Chris@14 66
Chris@14 67 $template->renderTo($file);
Chris@14 68 }
Chris@14 69
Chris@14 70 /**
Chris@14 71 * @param FileNode $node
Chris@14 72 *
Chris@14 73 * @return string
Chris@14 74 */
Chris@14 75 protected function renderItems(FileNode $node)
Chris@14 76 {
Chris@14 77 $template = new \Text_Template($this->templatePath . 'file_item.html', '{{', '}}');
Chris@14 78
Chris@14 79 $methodItemTemplate = new \Text_Template(
Chris@14 80 $this->templatePath . 'method_item.html',
Chris@14 81 '{{',
Chris@14 82 '}}'
Chris@14 83 );
Chris@14 84
Chris@14 85 $items = $this->renderItemTemplate(
Chris@14 86 $template,
Chris@14 87 [
Chris@14 88 'name' => 'Total',
Chris@14 89 'numClasses' => $node->getNumClassesAndTraits(),
Chris@14 90 'numTestedClasses' => $node->getNumTestedClassesAndTraits(),
Chris@14 91 'numMethods' => $node->getNumFunctionsAndMethods(),
Chris@14 92 'numTestedMethods' => $node->getNumTestedFunctionsAndMethods(),
Chris@14 93 'linesExecutedPercent' => $node->getLineExecutedPercent(false),
Chris@14 94 'linesExecutedPercentAsString' => $node->getLineExecutedPercent(),
Chris@14 95 'numExecutedLines' => $node->getNumExecutedLines(),
Chris@14 96 'numExecutableLines' => $node->getNumExecutableLines(),
Chris@14 97 'testedMethodsPercent' => $node->getTestedFunctionsAndMethodsPercent(false),
Chris@14 98 'testedMethodsPercentAsString' => $node->getTestedFunctionsAndMethodsPercent(),
Chris@14 99 'testedClassesPercent' => $node->getTestedClassesAndTraitsPercent(false),
Chris@14 100 'testedClassesPercentAsString' => $node->getTestedClassesAndTraitsPercent(),
Chris@14 101 'crap' => '<abbr title="Change Risk Anti-Patterns (CRAP) Index">CRAP</abbr>'
Chris@14 102 ]
Chris@14 103 );
Chris@14 104
Chris@14 105 $items .= $this->renderFunctionItems(
Chris@14 106 $node->getFunctions(),
Chris@14 107 $methodItemTemplate
Chris@14 108 );
Chris@14 109
Chris@14 110 $items .= $this->renderTraitOrClassItems(
Chris@14 111 $node->getTraits(),
Chris@14 112 $template,
Chris@14 113 $methodItemTemplate
Chris@14 114 );
Chris@14 115
Chris@14 116 $items .= $this->renderTraitOrClassItems(
Chris@14 117 $node->getClasses(),
Chris@14 118 $template,
Chris@14 119 $methodItemTemplate
Chris@14 120 );
Chris@14 121
Chris@14 122 return $items;
Chris@14 123 }
Chris@14 124
Chris@14 125 /**
Chris@14 126 * @param array $items
Chris@14 127 * @param \Text_Template $template
Chris@14 128 * @param \Text_Template $methodItemTemplate
Chris@14 129 *
Chris@14 130 * @return string
Chris@14 131 */
Chris@14 132 protected function renderTraitOrClassItems(array $items, \Text_Template $template, \Text_Template $methodItemTemplate)
Chris@14 133 {
Chris@14 134 $buffer = '';
Chris@14 135
Chris@14 136 if (empty($items)) {
Chris@14 137 return $buffer;
Chris@14 138 }
Chris@14 139
Chris@14 140 foreach ($items as $name => $item) {
Chris@14 141 $numMethods = 0;
Chris@14 142 $numTestedMethods = 0;
Chris@14 143
Chris@14 144 foreach ($item['methods'] as $method) {
Chris@14 145 if ($method['executableLines'] > 0) {
Chris@14 146 $numMethods++;
Chris@14 147
Chris@14 148 if ($method['executedLines'] === $method['executableLines']) {
Chris@14 149 $numTestedMethods++;
Chris@14 150 }
Chris@14 151 }
Chris@14 152 }
Chris@14 153
Chris@14 154 if ($item['executableLines'] > 0) {
Chris@14 155 $numClasses = 1;
Chris@14 156 $numTestedClasses = $numTestedMethods == $numMethods ? 1 : 0;
Chris@14 157 $linesExecutedPercentAsString = Util::percent(
Chris@14 158 $item['executedLines'],
Chris@14 159 $item['executableLines'],
Chris@14 160 true
Chris@14 161 );
Chris@14 162 } else {
Chris@14 163 $numClasses = 'n/a';
Chris@14 164 $numTestedClasses = 'n/a';
Chris@14 165 $linesExecutedPercentAsString = 'n/a';
Chris@14 166 }
Chris@14 167
Chris@14 168 $buffer .= $this->renderItemTemplate(
Chris@14 169 $template,
Chris@14 170 [
Chris@14 171 'name' => $name,
Chris@14 172 'numClasses' => $numClasses,
Chris@14 173 'numTestedClasses' => $numTestedClasses,
Chris@14 174 'numMethods' => $numMethods,
Chris@14 175 'numTestedMethods' => $numTestedMethods,
Chris@14 176 'linesExecutedPercent' => Util::percent(
Chris@14 177 $item['executedLines'],
Chris@14 178 $item['executableLines'],
Chris@14 179 false
Chris@14 180 ),
Chris@14 181 'linesExecutedPercentAsString' => $linesExecutedPercentAsString,
Chris@14 182 'numExecutedLines' => $item['executedLines'],
Chris@14 183 'numExecutableLines' => $item['executableLines'],
Chris@14 184 'testedMethodsPercent' => Util::percent(
Chris@14 185 $numTestedMethods,
Chris@14 186 $numMethods,
Chris@14 187 false
Chris@14 188 ),
Chris@14 189 'testedMethodsPercentAsString' => Util::percent(
Chris@14 190 $numTestedMethods,
Chris@14 191 $numMethods,
Chris@14 192 true
Chris@14 193 ),
Chris@14 194 'testedClassesPercent' => Util::percent(
Chris@14 195 $numTestedMethods == $numMethods ? 1 : 0,
Chris@14 196 1,
Chris@14 197 false
Chris@14 198 ),
Chris@14 199 'testedClassesPercentAsString' => Util::percent(
Chris@14 200 $numTestedMethods == $numMethods ? 1 : 0,
Chris@14 201 1,
Chris@14 202 true
Chris@14 203 ),
Chris@14 204 'crap' => $item['crap']
Chris@14 205 ]
Chris@14 206 );
Chris@14 207
Chris@14 208 foreach ($item['methods'] as $method) {
Chris@14 209 $buffer .= $this->renderFunctionOrMethodItem(
Chris@14 210 $methodItemTemplate,
Chris@14 211 $method,
Chris@14 212 '&nbsp;'
Chris@14 213 );
Chris@14 214 }
Chris@14 215 }
Chris@14 216
Chris@14 217 return $buffer;
Chris@14 218 }
Chris@14 219
Chris@14 220 /**
Chris@14 221 * @param array $functions
Chris@14 222 * @param \Text_Template $template
Chris@14 223 *
Chris@14 224 * @return string
Chris@14 225 */
Chris@14 226 protected function renderFunctionItems(array $functions, \Text_Template $template)
Chris@14 227 {
Chris@14 228 if (empty($functions)) {
Chris@14 229 return '';
Chris@14 230 }
Chris@14 231
Chris@14 232 $buffer = '';
Chris@14 233
Chris@14 234 foreach ($functions as $function) {
Chris@14 235 $buffer .= $this->renderFunctionOrMethodItem(
Chris@14 236 $template,
Chris@14 237 $function
Chris@14 238 );
Chris@14 239 }
Chris@14 240
Chris@14 241 return $buffer;
Chris@14 242 }
Chris@14 243
Chris@14 244 /**
Chris@14 245 * @param \Text_Template $template
Chris@14 246 *
Chris@14 247 * @return string
Chris@14 248 */
Chris@14 249 protected function renderFunctionOrMethodItem(\Text_Template $template, array $item, $indent = '')
Chris@14 250 {
Chris@14 251 $numMethods = 0;
Chris@14 252 $numTestedMethods = 0;
Chris@14 253
Chris@14 254 if ($item['executableLines'] > 0) {
Chris@14 255 $numMethods = 1;
Chris@14 256
Chris@14 257 if ($item['executedLines'] === $item['executableLines']) {
Chris@14 258 $numTestedMethods = 1;
Chris@14 259 }
Chris@14 260 }
Chris@14 261
Chris@14 262 return $this->renderItemTemplate(
Chris@14 263 $template,
Chris@14 264 [
Chris@14 265 'name' => \sprintf(
Chris@14 266 '%s<a href="#%d"><abbr title="%s">%s</abbr></a>',
Chris@14 267 $indent,
Chris@14 268 $item['startLine'],
Chris@14 269 \htmlspecialchars($item['signature']),
Chris@14 270 isset($item['functionName']) ? $item['functionName'] : $item['methodName']
Chris@14 271 ),
Chris@14 272 'numMethods' => $numMethods,
Chris@14 273 'numTestedMethods' => $numTestedMethods,
Chris@14 274 'linesExecutedPercent' => Util::percent(
Chris@14 275 $item['executedLines'],
Chris@14 276 $item['executableLines'],
Chris@14 277 false
Chris@14 278 ),
Chris@14 279 'linesExecutedPercentAsString' => Util::percent(
Chris@14 280 $item['executedLines'],
Chris@14 281 $item['executableLines'],
Chris@14 282 true
Chris@14 283 ),
Chris@14 284 'numExecutedLines' => $item['executedLines'],
Chris@14 285 'numExecutableLines' => $item['executableLines'],
Chris@14 286 'testedMethodsPercent' => Util::percent(
Chris@14 287 $numTestedMethods,
Chris@14 288 1,
Chris@14 289 false
Chris@14 290 ),
Chris@14 291 'testedMethodsPercentAsString' => Util::percent(
Chris@14 292 $numTestedMethods,
Chris@14 293 1,
Chris@14 294 true
Chris@14 295 ),
Chris@14 296 'crap' => $item['crap']
Chris@14 297 ]
Chris@14 298 );
Chris@14 299 }
Chris@14 300
Chris@14 301 /**
Chris@14 302 * @param FileNode $node
Chris@14 303 *
Chris@14 304 * @return string
Chris@14 305 */
Chris@14 306 protected function renderSource(FileNode $node)
Chris@14 307 {
Chris@14 308 $coverageData = $node->getCoverageData();
Chris@14 309 $testData = $node->getTestData();
Chris@14 310 $codeLines = $this->loadFile($node->getPath());
Chris@14 311 $lines = '';
Chris@14 312 $i = 1;
Chris@14 313
Chris@14 314 foreach ($codeLines as $line) {
Chris@14 315 $trClass = '';
Chris@14 316 $popoverContent = '';
Chris@14 317 $popoverTitle = '';
Chris@14 318
Chris@14 319 if (\array_key_exists($i, $coverageData)) {
Chris@14 320 $numTests = ($coverageData[$i] ? \count($coverageData[$i]) : 0);
Chris@14 321
Chris@14 322 if ($coverageData[$i] === null) {
Chris@14 323 $trClass = ' class="warning"';
Chris@14 324 } elseif ($numTests == 0) {
Chris@14 325 $trClass = ' class="danger"';
Chris@14 326 } else {
Chris@14 327 $lineCss = 'covered-by-large-tests';
Chris@14 328 $popoverContent = '<ul>';
Chris@14 329
Chris@14 330 if ($numTests > 1) {
Chris@14 331 $popoverTitle = $numTests . ' tests cover line ' . $i;
Chris@14 332 } else {
Chris@14 333 $popoverTitle = '1 test covers line ' . $i;
Chris@14 334 }
Chris@14 335
Chris@14 336 foreach ($coverageData[$i] as $test) {
Chris@14 337 if ($lineCss == 'covered-by-large-tests' && $testData[$test]['size'] == 'medium') {
Chris@14 338 $lineCss = 'covered-by-medium-tests';
Chris@14 339 } elseif ($testData[$test]['size'] == 'small') {
Chris@14 340 $lineCss = 'covered-by-small-tests';
Chris@14 341 }
Chris@14 342
Chris@14 343 switch ($testData[$test]['status']) {
Chris@14 344 case 0:
Chris@14 345 switch ($testData[$test]['size']) {
Chris@14 346 case 'small':
Chris@14 347 $testCSS = ' class="covered-by-small-tests"';
Chris@14 348
Chris@14 349 break;
Chris@14 350
Chris@14 351 case 'medium':
Chris@14 352 $testCSS = ' class="covered-by-medium-tests"';
Chris@14 353
Chris@14 354 break;
Chris@14 355
Chris@14 356 default:
Chris@14 357 $testCSS = ' class="covered-by-large-tests"';
Chris@14 358
Chris@14 359 break;
Chris@14 360 }
Chris@14 361
Chris@14 362 break;
Chris@14 363
Chris@14 364 case 1:
Chris@14 365 case 2:
Chris@14 366 $testCSS = ' class="warning"';
Chris@14 367
Chris@14 368 break;
Chris@14 369
Chris@14 370 case 3:
Chris@14 371 $testCSS = ' class="danger"';
Chris@14 372
Chris@14 373 break;
Chris@14 374
Chris@14 375 case 4:
Chris@14 376 $testCSS = ' class="danger"';
Chris@14 377
Chris@14 378 break;
Chris@14 379
Chris@14 380 default:
Chris@14 381 $testCSS = '';
Chris@14 382 }
Chris@14 383
Chris@14 384 $popoverContent .= \sprintf(
Chris@14 385 '<li%s>%s</li>',
Chris@14 386 $testCSS,
Chris@14 387 \htmlspecialchars($test)
Chris@14 388 );
Chris@14 389 }
Chris@14 390
Chris@14 391 $popoverContent .= '</ul>';
Chris@14 392 $trClass = ' class="' . $lineCss . ' popin"';
Chris@14 393 }
Chris@14 394 }
Chris@14 395
Chris@14 396 if (!empty($popoverTitle)) {
Chris@14 397 $popover = \sprintf(
Chris@14 398 ' data-title="%s" data-content="%s" data-placement="bottom" data-html="true"',
Chris@14 399 $popoverTitle,
Chris@14 400 \htmlspecialchars($popoverContent)
Chris@14 401 );
Chris@14 402 } else {
Chris@14 403 $popover = '';
Chris@14 404 }
Chris@14 405
Chris@14 406 $lines .= \sprintf(
Chris@14 407 ' <tr%s%s><td><div align="right"><a name="%d"></a><a href="#%d">%d</a></div></td><td class="codeLine">%s</td></tr>' . "\n",
Chris@14 408 $trClass,
Chris@14 409 $popover,
Chris@14 410 $i,
Chris@14 411 $i,
Chris@14 412 $i,
Chris@14 413 $line
Chris@14 414 );
Chris@14 415
Chris@14 416 $i++;
Chris@14 417 }
Chris@14 418
Chris@14 419 return $lines;
Chris@14 420 }
Chris@14 421
Chris@14 422 /**
Chris@14 423 * @param string $file
Chris@14 424 *
Chris@14 425 * @return array
Chris@14 426 */
Chris@14 427 protected function loadFile($file)
Chris@14 428 {
Chris@14 429 $buffer = \file_get_contents($file);
Chris@14 430 $tokens = \token_get_all($buffer);
Chris@14 431 $result = [''];
Chris@14 432 $i = 0;
Chris@14 433 $stringFlag = false;
Chris@14 434 $fileEndsWithNewLine = \substr($buffer, -1) == "\n";
Chris@14 435
Chris@14 436 unset($buffer);
Chris@14 437
Chris@14 438 foreach ($tokens as $j => $token) {
Chris@14 439 if (\is_string($token)) {
Chris@14 440 if ($token === '"' && $tokens[$j - 1] !== '\\') {
Chris@14 441 $result[$i] .= \sprintf(
Chris@14 442 '<span class="string">%s</span>',
Chris@14 443 \htmlspecialchars($token)
Chris@14 444 );
Chris@14 445
Chris@14 446 $stringFlag = !$stringFlag;
Chris@14 447 } else {
Chris@14 448 $result[$i] .= \sprintf(
Chris@14 449 '<span class="keyword">%s</span>',
Chris@14 450 \htmlspecialchars($token)
Chris@14 451 );
Chris@14 452 }
Chris@14 453
Chris@14 454 continue;
Chris@14 455 }
Chris@14 456
Chris@14 457 list($token, $value) = $token;
Chris@14 458
Chris@14 459 $value = \str_replace(
Chris@14 460 ["\t", ' '],
Chris@14 461 ['&nbsp;&nbsp;&nbsp;&nbsp;', '&nbsp;'],
Chris@14 462 \htmlspecialchars($value, $this->htmlspecialcharsFlags)
Chris@14 463 );
Chris@14 464
Chris@14 465 if ($value === "\n") {
Chris@14 466 $result[++$i] = '';
Chris@14 467 } else {
Chris@14 468 $lines = \explode("\n", $value);
Chris@14 469
Chris@14 470 foreach ($lines as $jj => $line) {
Chris@14 471 $line = \trim($line);
Chris@14 472
Chris@14 473 if ($line !== '') {
Chris@14 474 if ($stringFlag) {
Chris@14 475 $colour = 'string';
Chris@14 476 } else {
Chris@14 477 switch ($token) {
Chris@14 478 case T_INLINE_HTML:
Chris@14 479 $colour = 'html';
Chris@14 480
Chris@14 481 break;
Chris@14 482
Chris@14 483 case T_COMMENT:
Chris@14 484 case T_DOC_COMMENT:
Chris@14 485 $colour = 'comment';
Chris@14 486
Chris@14 487 break;
Chris@14 488
Chris@14 489 case T_ABSTRACT:
Chris@14 490 case T_ARRAY:
Chris@14 491 case T_AS:
Chris@14 492 case T_BREAK:
Chris@14 493 case T_CALLABLE:
Chris@14 494 case T_CASE:
Chris@14 495 case T_CATCH:
Chris@14 496 case T_CLASS:
Chris@14 497 case T_CLONE:
Chris@14 498 case T_CONTINUE:
Chris@14 499 case T_DEFAULT:
Chris@14 500 case T_ECHO:
Chris@14 501 case T_ELSE:
Chris@14 502 case T_ELSEIF:
Chris@14 503 case T_EMPTY:
Chris@14 504 case T_ENDDECLARE:
Chris@14 505 case T_ENDFOR:
Chris@14 506 case T_ENDFOREACH:
Chris@14 507 case T_ENDIF:
Chris@14 508 case T_ENDSWITCH:
Chris@14 509 case T_ENDWHILE:
Chris@14 510 case T_EXIT:
Chris@14 511 case T_EXTENDS:
Chris@14 512 case T_FINAL:
Chris@14 513 case T_FINALLY:
Chris@14 514 case T_FOREACH:
Chris@14 515 case T_FUNCTION:
Chris@14 516 case T_GLOBAL:
Chris@14 517 case T_IF:
Chris@14 518 case T_IMPLEMENTS:
Chris@14 519 case T_INCLUDE:
Chris@14 520 case T_INCLUDE_ONCE:
Chris@14 521 case T_INSTANCEOF:
Chris@14 522 case T_INSTEADOF:
Chris@14 523 case T_INTERFACE:
Chris@14 524 case T_ISSET:
Chris@14 525 case T_LOGICAL_AND:
Chris@14 526 case T_LOGICAL_OR:
Chris@14 527 case T_LOGICAL_XOR:
Chris@14 528 case T_NAMESPACE:
Chris@14 529 case T_NEW:
Chris@14 530 case T_PRIVATE:
Chris@14 531 case T_PROTECTED:
Chris@14 532 case T_PUBLIC:
Chris@14 533 case T_REQUIRE:
Chris@14 534 case T_REQUIRE_ONCE:
Chris@14 535 case T_RETURN:
Chris@14 536 case T_STATIC:
Chris@14 537 case T_THROW:
Chris@14 538 case T_TRAIT:
Chris@14 539 case T_TRY:
Chris@14 540 case T_UNSET:
Chris@14 541 case T_USE:
Chris@14 542 case T_VAR:
Chris@14 543 case T_WHILE:
Chris@14 544 case T_YIELD:
Chris@14 545 $colour = 'keyword';
Chris@14 546
Chris@14 547 break;
Chris@14 548
Chris@14 549 default:
Chris@14 550 $colour = 'default';
Chris@14 551 }
Chris@14 552 }
Chris@14 553
Chris@14 554 $result[$i] .= \sprintf(
Chris@14 555 '<span class="%s">%s</span>',
Chris@14 556 $colour,
Chris@14 557 $line
Chris@14 558 );
Chris@14 559 }
Chris@14 560
Chris@14 561 if (isset($lines[$jj + 1])) {
Chris@14 562 $result[++$i] = '';
Chris@14 563 }
Chris@14 564 }
Chris@14 565 }
Chris@14 566 }
Chris@14 567
Chris@14 568 if ($fileEndsWithNewLine) {
Chris@14 569 unset($result[\count($result) - 1]);
Chris@14 570 }
Chris@14 571
Chris@14 572 return $result;
Chris@14 573 }
Chris@14 574 }