5 * Copyright (c) 2009-2011, Sebastian Bergmann <sb@sebastian-bergmann.de>.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
12 * * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * * Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
20 * * Neither the name of Sebastian Bergmann nor the names of his
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
38 * @package CodeCoverage
39 * @author Sebastian Bergmann <sb@sebastian-bergmann.de>
40 * @copyright 2009-2011 Sebastian Bergmann <sb@sebastian-bergmann.de>
41 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
42 * @link http://github.com/sebastianbergmann/php-code-coverage
43 * @since File available since Release 1.0.0
47 * Base class for nodes in the code coverage information tree.
50 * @package CodeCoverage
51 * @author Sebastian Bergmann <sb@sebastian-bergmann.de>
52 * @copyright 2009-2011 Sebastian Bergmann <sb@sebastian-bergmann.de>
53 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
54 * @version Release: 1.0.4
55 * @link http://github.com/sebastianbergmann/php-code-coverage
56 * @since Class available since Release 1.0.0
58 abstract class PHP_CodeCoverage_Report_HTML_Node
63 protected $cache = array();
71 * @var PHP_CodeCoverage_Report_HTML_Node
79 * @param PHP_CodeCoverage_Report_HTML_Node $parent
81 public function __construct($name, PHP_CodeCoverage_Report_HTML_Node $parent = NULL)
84 $this->parent = $parent;
88 * Returns the percentage of classes that has been tested.
92 public function getTestedClassesPercent()
94 return PHP_CodeCoverage_Util::percent(
95 $this->getNumTestedClasses(),
96 $this->getNumClasses(),
102 * Returns the percentage of methods that has been tested.
106 public function getTestedMethodsPercent()
108 return PHP_CodeCoverage_Util::percent(
109 $this->getNumTestedMethods(),
110 $this->getNumMethods(),
116 * Returns the percentage of executed lines.
120 public function getLineExecutedPercent()
122 return PHP_CodeCoverage_Util::percent(
123 $this->getNumExecutedLines(),
124 $this->getNumExecutableLines(),
130 * Returns this node's ID.
134 public function getId()
136 if (!isset($this->cache['id'])) {
137 if ($this->parent === NULL) {
138 $this->cache['id'] = 'index';
140 $parentId = $this->parent->getId();
142 if ($parentId == 'index') {
143 $this->cache['id'] = $this->getName();
145 $this->cache['id'] = $parentId . '_' . $this->getName();
150 return $this->cache['id'];
154 * Returns this node's name.
156 * @param boolean $includeParent
159 public function getName($includeParent = FALSE, $includeCommonPath = FALSE)
161 if ($includeParent && $this->parent !== NULL) {
162 if (!isset($this->cache['nameIncludingParent'])) {
163 $parent = $this->parent->getName(TRUE);
165 if (!empty($parent)) {
166 $this->cache['nameIncludingParent'] = $parent . '/' .
169 $this->cache['nameIncludingParent'] = $this->name;
173 return $this->cache['nameIncludingParent'];
175 if ($this->parent !== NULL) {
178 return $includeCommonPath ? $this->name : '';
184 * Returns the link to this node.
186 * @param boolean $full
189 public function getLink($full)
191 if (substr($this->name, -1) == DIRECTORY_SEPARATOR) {
192 $name = substr($this->name, 0, -1);
197 $cleanId = PHP_CodeCoverage_Util::getSafeFilename($this->getId());
200 if ($this->parent !== NULL) {
201 $parent = $this->parent->getLink(TRUE) . DIRECTORY_SEPARATOR;
207 '%s<a href="%s.html">%s</a>',
214 '<a href="%s.html">%s</a>',
222 * Returns this node's path.
226 public function getPath()
228 if (!isset($this->cache['path'])) {
229 if ($this->parent === NULL) {
230 $this->cache['path'] = $this->getName(FALSE, TRUE);
232 $parentPath = $this->parent->getPath();
234 if (substr($parentPath, -1) == DIRECTORY_SEPARATOR) {
235 $this->cache['path'] = $parentPath .
236 $this->getName(FALSE, TRUE);
238 $this->cache['path'] = $parentPath .
239 DIRECTORY_SEPARATOR .
240 $this->getName(FALSE, TRUE);
242 if ($parentPath === '' &&
243 realpath($this->cache['path']) === FALSE &&
244 realpath($this->getName(FALSE, TRUE)) !== FALSE) {
245 $this->cache['path'] = $this->getName(FALSE, TRUE);
251 return $this->cache['path'];
254 protected function doRenderItemObject(PHP_CodeCoverage_Report_HTML_Node $item, $lowUpperBound, $highLowerBound, $link = NULL, $itemClass = 'coverItem')
256 return $this->doRenderItem(
258 'name' => $link != NULL ? $link : $item->getLink(
261 'itemClass' => $itemClass,
262 'numClasses' => $item->getNumClasses(),
263 'numTestedClasses' => $item->getNumTestedClasses(),
264 'testedClassesPercent' => $item->getTestedClassesPercent(),
265 'numMethods' => $item->getNumMethods(),
266 'numTestedMethods' => $item->getNumTestedMethods(),
267 'testedMethodsPercent' => $item->getTestedMethodsPercent(),
268 'numExecutableLines' => $item->getNumExecutableLines(),
269 'numExecutedLines' => $item->getNumExecutedLines(),
270 'executedLinesPercent' => $item->getLineExecutedPercent(),
271 'crap' => $link == 'Total' ? '<acronym title="Change Risk Anti-Patterns (CRAP) Index">CRAP</acronym>' : ''
278 protected function doRenderItem(array $data, $lowUpperBound, $highLowerBound, $template = NULL)
280 if ($template === NULL) {
281 if ($this instanceof PHP_CodeCoverage_Report_HTML_Node_Directory) {
282 $template = 'directory_item.html';
284 $template = 'file_item.html';
288 $itemTemplate = new Text_Template(
289 PHP_CodeCoverage_Report_HTML::$templatePath . $template
292 if ($data['numClasses'] > 0) {
293 list($classesColor, $classesLevel) = $this->getColorLevel(
294 $data['testedClassesPercent'], $lowUpperBound, $highLowerBound
297 $classesNumber = $data['numTestedClasses'] . ' / ' .
300 $classesColor = 'snow';
301 $classesLevel = 'None';
302 $classesNumber = ' ';
305 if ($data['numMethods'] > 0) {
306 list($methodsColor, $methodsLevel) = $this->getColorLevel(
307 $data['testedMethodsPercent'], $lowUpperBound, $highLowerBound
310 $methodsNumber = $data['numTestedMethods'] . ' / ' .
313 $methodsColor = 'snow';
314 $methodsLevel = 'None';
315 $methodsNumber = ' ';
318 list($linesColor, $linesLevel) = $this->getColorLevel(
319 $data['executedLinesPercent'], $lowUpperBound, $highLowerBound
322 if ($data['name'] == '<b><a href="#0">*</a></b>') {
330 if (isset($data['itemClass'])) {
331 if ($data['itemClass'] == 'coverDirectory') {
332 $icon = '<img alt="directory" src="directory.png"/> ';
335 else if ($data['itemClass'] == 'coverFile') {
336 $icon = '<img alt="file" src="file.png"/> ';
340 $itemTemplate->setVar(
342 'name' => $functions ? 'Functions' : $data['name'],
344 'itemClass' => isset($data['itemClass']) ? $data['itemClass'] : 'coverItem',
345 'classes_color' => $classesColor,
346 'classes_level' => $functions ? 'None' : $classesLevel,
347 'classes_tested_width' => floor($data['testedClassesPercent']),
348 'classes_tested_percent' => !$functions && $data['numClasses'] > 0 ? $data['testedClassesPercent'] . '%' : ' ',
349 'classes_not_tested_width' => 100 - floor($data['testedClassesPercent']),
350 'classes_number' => $functions ? ' ' : $classesNumber,
351 'methods_color' => $methodsColor,
352 'methods_level' => $methodsLevel,
353 'methods_tested_width' => floor($data['testedMethodsPercent']),
354 'methods_tested_percent' => $data['numMethods'] > 0 ? $data['testedMethodsPercent'] . '%' : ' ',
355 'methods_not_tested_width' => 100 - floor($data['testedMethodsPercent']),
356 'methods_number' => $methodsNumber,
357 'lines_color' => $linesColor,
358 'lines_level' => $linesLevel,
359 'lines_executed_width' => floor($data['executedLinesPercent']),
360 'lines_executed_percent' => $data['executedLinesPercent'] . '%',
361 'lines_not_executed_width' => 100 - floor($data['executedLinesPercent']),
362 'num_executable_lines' => $data['numExecutableLines'],
363 'num_executed_lines' => $data['numExecutedLines'],
364 'crap' => isset($data['crap']) ? $data['crap'] : ''
368 return $itemTemplate->render();
371 protected function getColorLevel($percent, $lowUpperBound, $highLowerBound)
373 $floorPercent = floor($percent);
375 if ($floorPercent < $lowUpperBound) {
376 $color = 'scarlet_red';
380 else if ($floorPercent >= $lowUpperBound &&
381 $floorPercent < $highLowerBound) {
387 $color = 'chameleon';
391 return array($color, $level);
394 protected function renderTotalItem($lowUpperBound, $highLowerBound, $directory = TRUE)
397 empty($this->directories) &&
398 count($this->files) == 1) {
402 return $this->doRenderItemObject(
403 $this, $lowUpperBound, $highLowerBound, 'Total'
406 ' <td class="tableHead" colspan="11"> </td>' .
411 * @param Text_Template $template
412 * @param string $title
413 * @param string $charset
414 * @param string $generator
416 protected function setTemplateVars(Text_Template $template, $title, $charset, $generator)
420 if ($this instanceof PHP_CodeCoverage_Report_HTML_Node_Directory) {
421 $dashboard = sprintf(
422 '<a href="%s">dashboard</a>',
423 PHP_CodeCoverage_Util::getSafeFilename(
425 ) . '.dashboard.html'
432 'charset' => $charset,
433 'link' => $this->getLink(TRUE),
434 'dashboard_link' => $dashboard,
435 'num_executable_lines' => $this->getNumExecutableLines(),
436 'num_executed_lines' => $this->getNumExecutedLines(),
437 'lines_executed_percent' => $this->getLineExecutedPercent(),
440 $_SERVER['REQUEST_TIME']
442 'version' => '1.0.4',
443 'php_version' => PHP_VERSION,
444 'generator' => $generator
450 * Returns the classes of this node.
454 abstract public function getClasses();
457 * Returns the number of executable lines.
461 abstract public function getNumExecutableLines();
464 * Returns the number of executed lines.
468 abstract public function getNumExecutedLines();
471 * Returns the number of classes.
475 abstract public function getNumClasses();
478 * Returns the number of tested classes.
482 abstract public function getNumTestedClasses();
485 * Returns the number of methods.
489 abstract public function getNumMethods();
492 * Returns the number of tested methods.
496 abstract public function getNumTestedMethods();
501 * @param string $target
502 * @param string $title
503 * @param string $charset
504 * @param integer $lowUpperBound
505 * @param integer $highLowerBound
506 * @param string $generator
508 abstract public function render($target, $title, $charset = 'UTF-8', $lowUpperBound = 35, $highLowerBound = 70, $generator = '');
511 require_once 'PHP/CodeCoverage/Report/HTML/Node/Directory.php';
512 require_once 'PHP/CodeCoverage/Report/HTML/Node/File.php';