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