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\AbstractNode;
|
Chris@14
|
14 use SebastianBergmann\CodeCoverage\Node\Directory as DirectoryNode;
|
Chris@14
|
15 use SebastianBergmann\CodeCoverage\Node\File as FileNode;
|
Chris@14
|
16 use SebastianBergmann\CodeCoverage\Version;
|
Chris@14
|
17 use SebastianBergmann\Environment\Runtime;
|
Chris@14
|
18
|
Chris@14
|
19 /**
|
Chris@14
|
20 * Base class for node renderers.
|
Chris@14
|
21 */
|
Chris@14
|
22 abstract class Renderer
|
Chris@14
|
23 {
|
Chris@14
|
24 /**
|
Chris@14
|
25 * @var string
|
Chris@14
|
26 */
|
Chris@14
|
27 protected $templatePath;
|
Chris@14
|
28
|
Chris@14
|
29 /**
|
Chris@14
|
30 * @var string
|
Chris@14
|
31 */
|
Chris@14
|
32 protected $generator;
|
Chris@14
|
33
|
Chris@14
|
34 /**
|
Chris@14
|
35 * @var string
|
Chris@14
|
36 */
|
Chris@14
|
37 protected $date;
|
Chris@14
|
38
|
Chris@14
|
39 /**
|
Chris@14
|
40 * @var int
|
Chris@14
|
41 */
|
Chris@14
|
42 protected $lowUpperBound;
|
Chris@14
|
43
|
Chris@14
|
44 /**
|
Chris@14
|
45 * @var int
|
Chris@14
|
46 */
|
Chris@14
|
47 protected $highLowerBound;
|
Chris@14
|
48
|
Chris@14
|
49 /**
|
Chris@14
|
50 * @var string
|
Chris@14
|
51 */
|
Chris@14
|
52 protected $version;
|
Chris@14
|
53
|
Chris@14
|
54 /**
|
Chris@14
|
55 * Constructor.
|
Chris@14
|
56 *
|
Chris@14
|
57 * @param string $templatePath
|
Chris@14
|
58 * @param string $generator
|
Chris@14
|
59 * @param string $date
|
Chris@14
|
60 * @param int $lowUpperBound
|
Chris@14
|
61 * @param int $highLowerBound
|
Chris@14
|
62 */
|
Chris@14
|
63 public function __construct($templatePath, $generator, $date, $lowUpperBound, $highLowerBound)
|
Chris@14
|
64 {
|
Chris@14
|
65 $this->templatePath = $templatePath;
|
Chris@14
|
66 $this->generator = $generator;
|
Chris@14
|
67 $this->date = $date;
|
Chris@14
|
68 $this->lowUpperBound = $lowUpperBound;
|
Chris@14
|
69 $this->highLowerBound = $highLowerBound;
|
Chris@14
|
70 $this->version = Version::id();
|
Chris@14
|
71 }
|
Chris@14
|
72
|
Chris@14
|
73 /**
|
Chris@14
|
74 * @param \Text_Template $template
|
Chris@14
|
75 * @param array $data
|
Chris@14
|
76 *
|
Chris@14
|
77 * @return string
|
Chris@14
|
78 */
|
Chris@14
|
79 protected function renderItemTemplate(\Text_Template $template, array $data)
|
Chris@14
|
80 {
|
Chris@14
|
81 $numSeparator = ' / ';
|
Chris@14
|
82
|
Chris@14
|
83 if (isset($data['numClasses']) && $data['numClasses'] > 0) {
|
Chris@14
|
84 $classesLevel = $this->getColorLevel($data['testedClassesPercent']);
|
Chris@14
|
85
|
Chris@14
|
86 $classesNumber = $data['numTestedClasses'] . $numSeparator .
|
Chris@14
|
87 $data['numClasses'];
|
Chris@14
|
88
|
Chris@14
|
89 $classesBar = $this->getCoverageBar(
|
Chris@14
|
90 $data['testedClassesPercent']
|
Chris@14
|
91 );
|
Chris@14
|
92 } else {
|
Chris@14
|
93 $classesLevel = '';
|
Chris@14
|
94 $classesNumber = '0' . $numSeparator . '0';
|
Chris@14
|
95 $classesBar = '';
|
Chris@14
|
96 $data['testedClassesPercentAsString'] = 'n/a';
|
Chris@14
|
97 }
|
Chris@14
|
98
|
Chris@14
|
99 if ($data['numMethods'] > 0) {
|
Chris@14
|
100 $methodsLevel = $this->getColorLevel($data['testedMethodsPercent']);
|
Chris@14
|
101
|
Chris@14
|
102 $methodsNumber = $data['numTestedMethods'] . $numSeparator .
|
Chris@14
|
103 $data['numMethods'];
|
Chris@14
|
104
|
Chris@14
|
105 $methodsBar = $this->getCoverageBar(
|
Chris@14
|
106 $data['testedMethodsPercent']
|
Chris@14
|
107 );
|
Chris@14
|
108 } else {
|
Chris@14
|
109 $methodsLevel = '';
|
Chris@14
|
110 $methodsNumber = '0' . $numSeparator . '0';
|
Chris@14
|
111 $methodsBar = '';
|
Chris@14
|
112 $data['testedMethodsPercentAsString'] = 'n/a';
|
Chris@14
|
113 }
|
Chris@14
|
114
|
Chris@14
|
115 if ($data['numExecutableLines'] > 0) {
|
Chris@14
|
116 $linesLevel = $this->getColorLevel($data['linesExecutedPercent']);
|
Chris@14
|
117
|
Chris@14
|
118 $linesNumber = $data['numExecutedLines'] . $numSeparator .
|
Chris@14
|
119 $data['numExecutableLines'];
|
Chris@14
|
120
|
Chris@14
|
121 $linesBar = $this->getCoverageBar(
|
Chris@14
|
122 $data['linesExecutedPercent']
|
Chris@14
|
123 );
|
Chris@14
|
124 } else {
|
Chris@14
|
125 $linesLevel = '';
|
Chris@14
|
126 $linesNumber = '0' . $numSeparator . '0';
|
Chris@14
|
127 $linesBar = '';
|
Chris@14
|
128 $data['linesExecutedPercentAsString'] = 'n/a';
|
Chris@14
|
129 }
|
Chris@14
|
130
|
Chris@14
|
131 $template->setVar(
|
Chris@14
|
132 [
|
Chris@14
|
133 'icon' => isset($data['icon']) ? $data['icon'] : '',
|
Chris@14
|
134 'crap' => isset($data['crap']) ? $data['crap'] : '',
|
Chris@14
|
135 'name' => $data['name'],
|
Chris@14
|
136 'lines_bar' => $linesBar,
|
Chris@14
|
137 'lines_executed_percent' => $data['linesExecutedPercentAsString'],
|
Chris@14
|
138 'lines_level' => $linesLevel,
|
Chris@14
|
139 'lines_number' => $linesNumber,
|
Chris@14
|
140 'methods_bar' => $methodsBar,
|
Chris@14
|
141 'methods_tested_percent' => $data['testedMethodsPercentAsString'],
|
Chris@14
|
142 'methods_level' => $methodsLevel,
|
Chris@14
|
143 'methods_number' => $methodsNumber,
|
Chris@14
|
144 'classes_bar' => $classesBar,
|
Chris@14
|
145 'classes_tested_percent' => isset($data['testedClassesPercentAsString']) ? $data['testedClassesPercentAsString'] : '',
|
Chris@14
|
146 'classes_level' => $classesLevel,
|
Chris@14
|
147 'classes_number' => $classesNumber
|
Chris@14
|
148 ]
|
Chris@14
|
149 );
|
Chris@14
|
150
|
Chris@14
|
151 return $template->render();
|
Chris@14
|
152 }
|
Chris@14
|
153
|
Chris@14
|
154 /**
|
Chris@14
|
155 * @param \Text_Template $template
|
Chris@14
|
156 * @param AbstractNode $node
|
Chris@14
|
157 */
|
Chris@14
|
158 protected function setCommonTemplateVariables(\Text_Template $template, AbstractNode $node)
|
Chris@14
|
159 {
|
Chris@14
|
160 $template->setVar(
|
Chris@14
|
161 [
|
Chris@14
|
162 'id' => $node->getId(),
|
Chris@14
|
163 'full_path' => $node->getPath(),
|
Chris@14
|
164 'path_to_root' => $this->getPathToRoot($node),
|
Chris@14
|
165 'breadcrumbs' => $this->getBreadcrumbs($node),
|
Chris@14
|
166 'date' => $this->date,
|
Chris@14
|
167 'version' => $this->version,
|
Chris@14
|
168 'runtime' => $this->getRuntimeString(),
|
Chris@14
|
169 'generator' => $this->generator,
|
Chris@14
|
170 'low_upper_bound' => $this->lowUpperBound,
|
Chris@14
|
171 'high_lower_bound' => $this->highLowerBound
|
Chris@14
|
172 ]
|
Chris@14
|
173 );
|
Chris@14
|
174 }
|
Chris@14
|
175
|
Chris@14
|
176 protected function getBreadcrumbs(AbstractNode $node)
|
Chris@14
|
177 {
|
Chris@14
|
178 $breadcrumbs = '';
|
Chris@14
|
179 $path = $node->getPathAsArray();
|
Chris@14
|
180 $pathToRoot = [];
|
Chris@14
|
181 $max = \count($path);
|
Chris@14
|
182
|
Chris@14
|
183 if ($node instanceof FileNode) {
|
Chris@14
|
184 $max--;
|
Chris@14
|
185 }
|
Chris@14
|
186
|
Chris@14
|
187 for ($i = 0; $i < $max; $i++) {
|
Chris@14
|
188 $pathToRoot[] = \str_repeat('../', $i);
|
Chris@14
|
189 }
|
Chris@14
|
190
|
Chris@14
|
191 foreach ($path as $step) {
|
Chris@14
|
192 if ($step !== $node) {
|
Chris@14
|
193 $breadcrumbs .= $this->getInactiveBreadcrumb(
|
Chris@14
|
194 $step,
|
Chris@14
|
195 \array_pop($pathToRoot)
|
Chris@14
|
196 );
|
Chris@14
|
197 } else {
|
Chris@14
|
198 $breadcrumbs .= $this->getActiveBreadcrumb($step);
|
Chris@14
|
199 }
|
Chris@14
|
200 }
|
Chris@14
|
201
|
Chris@14
|
202 return $breadcrumbs;
|
Chris@14
|
203 }
|
Chris@14
|
204
|
Chris@14
|
205 protected function getActiveBreadcrumb(AbstractNode $node)
|
Chris@14
|
206 {
|
Chris@14
|
207 $buffer = \sprintf(
|
Chris@14
|
208 ' <li class="active">%s</li>' . "\n",
|
Chris@14
|
209 $node->getName()
|
Chris@14
|
210 );
|
Chris@14
|
211
|
Chris@14
|
212 if ($node instanceof DirectoryNode) {
|
Chris@14
|
213 $buffer .= ' <li>(<a href="dashboard.html">Dashboard</a>)</li>' . "\n";
|
Chris@14
|
214 }
|
Chris@14
|
215
|
Chris@14
|
216 return $buffer;
|
Chris@14
|
217 }
|
Chris@14
|
218
|
Chris@14
|
219 protected function getInactiveBreadcrumb(AbstractNode $node, $pathToRoot)
|
Chris@14
|
220 {
|
Chris@14
|
221 return \sprintf(
|
Chris@14
|
222 ' <li><a href="%sindex.html">%s</a></li>' . "\n",
|
Chris@14
|
223 $pathToRoot,
|
Chris@14
|
224 $node->getName()
|
Chris@14
|
225 );
|
Chris@14
|
226 }
|
Chris@14
|
227
|
Chris@14
|
228 protected function getPathToRoot(AbstractNode $node)
|
Chris@14
|
229 {
|
Chris@14
|
230 $id = $node->getId();
|
Chris@14
|
231 $depth = \substr_count($id, '/');
|
Chris@14
|
232
|
Chris@14
|
233 if ($id != 'index' &&
|
Chris@14
|
234 $node instanceof DirectoryNode) {
|
Chris@14
|
235 $depth++;
|
Chris@14
|
236 }
|
Chris@14
|
237
|
Chris@14
|
238 return \str_repeat('../', $depth);
|
Chris@14
|
239 }
|
Chris@14
|
240
|
Chris@14
|
241 protected function getCoverageBar($percent)
|
Chris@14
|
242 {
|
Chris@14
|
243 $level = $this->getColorLevel($percent);
|
Chris@14
|
244
|
Chris@14
|
245 $template = new \Text_Template(
|
Chris@14
|
246 $this->templatePath . 'coverage_bar.html',
|
Chris@14
|
247 '{{',
|
Chris@14
|
248 '}}'
|
Chris@14
|
249 );
|
Chris@14
|
250
|
Chris@14
|
251 $template->setVar(['level' => $level, 'percent' => \sprintf('%.2F', $percent)]);
|
Chris@14
|
252
|
Chris@14
|
253 return $template->render();
|
Chris@14
|
254 }
|
Chris@14
|
255
|
Chris@14
|
256 /**
|
Chris@14
|
257 * @param int $percent
|
Chris@14
|
258 *
|
Chris@14
|
259 * @return string
|
Chris@14
|
260 */
|
Chris@14
|
261 protected function getColorLevel($percent)
|
Chris@14
|
262 {
|
Chris@14
|
263 if ($percent <= $this->lowUpperBound) {
|
Chris@14
|
264 return 'danger';
|
Chris@14
|
265 } elseif ($percent > $this->lowUpperBound &&
|
Chris@14
|
266 $percent < $this->highLowerBound) {
|
Chris@14
|
267 return 'warning';
|
Chris@14
|
268 } else {
|
Chris@14
|
269 return 'success';
|
Chris@14
|
270 }
|
Chris@14
|
271 }
|
Chris@14
|
272
|
Chris@14
|
273 /**
|
Chris@14
|
274 * @return string
|
Chris@14
|
275 */
|
Chris@14
|
276 private function getRuntimeString()
|
Chris@14
|
277 {
|
Chris@14
|
278 $runtime = new Runtime;
|
Chris@14
|
279
|
Chris@14
|
280 $buffer = \sprintf(
|
Chris@14
|
281 '<a href="%s" target="_top">%s %s</a>',
|
Chris@14
|
282 $runtime->getVendorUrl(),
|
Chris@14
|
283 $runtime->getName(),
|
Chris@14
|
284 $runtime->getVersion()
|
Chris@14
|
285 );
|
Chris@14
|
286
|
Chris@14
|
287 if ($runtime->hasXdebug() && !$runtime->hasPHPDBGCodeCoverage()) {
|
Chris@14
|
288 $buffer .= \sprintf(
|
Chris@14
|
289 ' with <a href="https://xdebug.org/">Xdebug %s</a>',
|
Chris@14
|
290 \phpversion('xdebug')
|
Chris@14
|
291 );
|
Chris@14
|
292 }
|
Chris@14
|
293
|
Chris@14
|
294 return $buffer;
|
Chris@14
|
295 }
|
Chris@14
|
296 }
|