[ Index ]

PHP Cross Reference of Limb3

title

Body

[close]

/tests_runner/lib/spikephpcoverage/src/reporter/ -> HtmlCoverageReporter.php (source)

   1  <?php
   2      /*
   3      *  $Id: HtmlCoverageReporter.php 14665 2005-03-23 19:37:50Z npac $
   4      *  
   5      *  Copyright(c) 2004-2005, SpikeSource Inc. All Rights Reserved.
   6      *  Licensed under the Open Source License version 2.1
   7      *  (See http://www.spikesource.com/license.html)
   8      */
   9  ?>
  10  <?php
  11  
  12      if(!defined("__PHPCOVERAGE_HOME")) {
  13          define("__PHPCOVERAGE_HOME", dirname(dirname(__FILE__)));
  14      }
  15      require_once  __PHPCOVERAGE_HOME . "/reporter/CoverageReporter.php";
  16      require_once __PHPCOVERAGE_HOME . "/parser/PHPParser.php";
  17      require_once  __PHPCOVERAGE_HOME . "/util/Utility.php";
  18  
  19      /** 
  20      * Class that implements HTML Coverage Reporter. 
  21      * 
  22      * @author Nimish Pachapurkar <npac@spikesource.com>
  23      * @version $Revision: 14665 $
  24      * @package tests_runner
  25      */
  26      class HtmlCoverageReporter extends CoverageReporter {
  27  
  28          /*{{{ Members */
  29  
  30          private $coverageData;
  31          private $htmlFile;
  32          private $body;
  33          private $header = "html/header.html";
  34          private $footer = "html/footer.html";
  35          private $indexHeader = "html/indexheader.html"; 
  36          private $indexFooter = "html/indexfooter.html";
  37  
  38          /*}}}*/
  39          /*{{{ public function __construct() */
  40  
  41          /** 
  42          * Constructor method (PHP5 only) 
  43          * 
  44          * @param $heading Heading of the report (shown as title)
  45          * @param $style Name of the stylesheet file
  46          * @param $dir Directory where the report files should be dumped
  47          * @access public
  48          */
  49          public function __construct(
  50              $heading="Coverage Report",
  51              $style="",
  52              $dir="report"
  53          ) {
  54              parent::__construct($heading, $style, $dir);
  55          }
  56  
  57          /*}}}*/
  58          /*{{{ public function generateReport() */
  59  
  60          /** 
  61          * Implementaion of generateReport abstract function.
  62          * This is the only function that will be called 
  63          * by the instrumentor.
  64          * 
  65          * @param &$data  Reference to Coverage Data
  66          * @access public
  67          */
  68          public function generateReport(&$data) {
  69              if(!file_exists($this->outputDir)) {
  70                  mkdir($this->outputDir);
  71              }
  72              $this->coverageData =& $data;
  73              $this->grandTotalFiles = count($this->coverageData);
  74              $ret = $this->writeIndexFile();
  75              if($ret === FALSE) {
  76                  $this->logger->error("Error occured!!!", __FILE__, __LINE__);
  77              }
  78              $this->logger->debug(print_r($data, true), __FILE__, __LINE__);
  79          }
  80  
  81          /*}}}*/
  82          /*{{{ private function writeIndexFileHeader() */
  83  
  84          /** 
  85          * Write the index file header to a string
  86          * 
  87          * @return string String containing HTML code for the index file header
  88          * @access private
  89          */
  90          private function writeIndexFileHeader() {
  91              $str = false;
  92              $dir = realpath(dirname(__FILE__));
  93              if($dir !== false) {
  94                  $str = file_get_contents($dir . "/" . $this->indexHeader);
  95                  if($str == false) {
  96                      return $str;
  97                  }
  98                  $str = str_replace("%%heading%%", $this->heading, $str);
  99                  $str = str_replace("%%style%%", $this->style, $str);
 100              }
 101              return $str;
 102          }
 103  
 104          /*}}}*/
 105          /*{{{ private function writeIndexFileFooter() */
 106  
 107          /** 
 108          * Write the index file footer to a string
 109          * 
 110          * @return string String containing HTML code for the index file footer.
 111          * @access private
 112          */
 113          private function writeIndexFileFooter() {
 114              $str = false;
 115              $dir = realpath(dirname(__FILE__));
 116              if($dir !== false) {
 117                  $str = file_get_contents($dir . "/" . $this->indexFooter);
 118                  if($str == false) {
 119                      return $str;
 120                  }
 121              }
 122              return $str;
 123          }
 124  
 125          /*}}}*/
 126          /*{{{ private function createJSDir() */
 127  
 128          /** 
 129          * Create a directory for storing Javascript for the report
 130          * 
 131          * @access private
 132          */
 133          private function createJSDir() {
 134              $jsDir = $this->outputDir . "/js";
 135              if(file_exists($this->outputDir) && !file_exists($jsDir)) {
 136                  mkdir($jsDir);
 137              }
 138              $jsSortFile = realpath(dirname(__FILE__)) . "/js/sort_spikesource.js";
 139              copy($jsSortFile, $jsDir . "/" . "sort_spikesource.js");
 140              return true;
 141          }
 142  
 143          /*}}}*/
 144          /*{{{ private function createImagesDir() */
 145  
 146          /** 
 147          * Create a directory for storing images for the report 
 148          * 
 149          * @access private
 150          */
 151          private function createImagesDir() {
 152              $imagesDir = $this->outputDir . "/images";
 153              if(file_exists($this->outputDir) && !file_exists($imagesDir)) {
 154                  mkdir($imagesDir);
 155              }
 156              $imagesSpikeDir = $imagesDir . "/spikesource";
 157              if(!file_exists($imagesSpikeDir)) {
 158                  mkdir($imagesSpikeDir);
 159              }
 160              $imagesArrowUpFile = realpath(dirname(__FILE__)) . "/images/arrow_up.gif";
 161              $imagesArrowDownFile = realpath(dirname(__FILE__)) . "/images/arrow_down.gif";
 162              $imagesPHPCoverageLogoFile = realpath(dirname(__FILE__)) . "/images/spikesource/phpcoverage.gif";
 163              $imagesSpacerFile = realpath(dirname(__FILE__)) . "/images/spacer.gif";
 164              copy($imagesArrowUpFile, $imagesDir . "/" . "arrow_up.gif");
 165              copy($imagesArrowDownFile, $imagesDir . "/" . "arrow_down.gif");
 166              copy($imagesSpacerFile, $imagesDir . "/" . "spacer.gif");
 167              copy($imagesPHPCoverageLogoFile, $imagesSpikeDir . "/" . "phpcoverage.gif");
 168              return true;
 169          }
 170  
 171          /*}}}*/
 172          /*{{{ private function createStyleDir() */
 173  
 174          private function createStyleDir() {
 175              if(empty($this->style)) {
 176                  $this->style = "spikesource.css";
 177              }
 178              $styleDir = $this->outputDir . "/css";
 179              if(file_exists($this->outputDir) && !file_exists($styleDir)) {
 180                  mkdir($styleDir);
 181              }
 182              $styleFile = realpath(dirname(__FILE__)) . "/css/" . $this->style;
 183              copy($styleFile, $styleDir . "/" . $this->style);
 184              return true;
 185          }
 186  
 187          /*}}}*/
 188          /*{{{ protected function writeIndexFileTableHead() */
 189  
 190          /** 
 191          * Writes the table heading for index.html 
 192          * 
 193          * @return string Table heading row code
 194          * @access protected
 195          */
 196          protected function writeIndexFileTableHead() {
 197              $str = "";
 198              $str .= '<h1>Details</h1> <table class="spikeDataTable" cellpadding="4" cellspacing="0" border="0" id="table2sort" width="800">';
 199              $str .= '<thead>';
 200              $str .= '<tr><td class="spikeDataTableHeadLeft" id="sortCell0" rowspan="2" style="white-space:nowrap" width="52%"><a id="sortCellLink0" class="headerlink" href="javascript:sort(0)" title="Sort Ascending">File Name </a></td>';
 201              $str .= '<td colspan="4" class="spikeDataTableHeadCenter">Lines</td>';
 202              $str .= '<td class="spikeDataTableHeadCenterLast" id="sortCell5" rowspan="2"  width="16%" style="white-space:nowrap"><a id="sortCellLink5" class="headerlink" href="javascript:sort(5, \'percentage\')" title="Sort Ascending">Code Coverage </a></td>';
 203              $str .= '</tr>';
 204  
 205              // Second row - subheadings
 206              $str .= '<tr>';
 207              $str .= '<td class="spikeDataTableSubHeadCenter" id="sortCell1" style="white-space:nowrap" width="8%"><a id="sortCellLink1" title="Sort Ascending" class="headerlink" href="javascript:sort(1, \'number\')">Total </a></td>';
 208              $str .= '<td class="spikeDataTableSubHeadCenter"  id="sortCell2" style="white-space:nowrap" width="9%"><a id="sortCellLink2" title="Sort Ascending" class="headerlink" href="javascript:sort(2, \'number\')">Covered </a></td>';
 209              $str .= '<td class="spikeDataTableSubHeadCenter" id="sortCell3" style="white-space:nowrap" width="8%"><a id="sortCellLink3" title="Sort Ascending" class="headerlink" href="javascript:sort(3, \'number\')">Missed </a></td>';
 210              $str .= '<td class="spikeDataTableSubHeadCenter" id="sortCell4" style="white-space:nowrap" width="10%"><a id="sortCellLink4" title="Sort Ascending" class="headerlink" href="javascript:sort(4, \'number\')">Executable </a></td>';
 211              $str .= '</tr>';
 212              $str .= '</thead>';
 213              return $str;
 214          }
 215  
 216          /*}}}*/
 217          /*{{{ protected function writeIndexFileTableRow() */
 218  
 219          /** 
 220          * Writes one row in the index.html table to display filename
 221          * and coverage recording.
 222          * 
 223          * @param $fileLink link to html details file.
 224          * @param $realFile path to real PHP file.
 225          * @param $fileCoverage Coverage recording for that file.
 226          * @return string HTML code for a single row.
 227          * @access protected
 228          */
 229          protected function writeIndexFileTableRow($fileLink, $realFile, $fileCoverage) {
 230  
 231              global $util;
 232              $fileLink = $this->makeRelative($fileLink);
 233              $realFileShort = $util->shortenFilename($realFile);
 234              $str = "";
 235  
 236              $str .= '<tr><td class="spikeDataTableCellLeft">';
 237              $str .= '<a class="contentlink" href="' . $util->unixifyPath($fileLink) . '" title="' 
 238              . $realFile .'">' . $realFileShort. '</a>' . '</td>';
 239              $str .= '<td class="spikeDataTableCellCenter">' . $fileCoverage['total'] . "</td>";
 240              $str .= '<td class="spikeDataTableCellCenter">' . $fileCoverage['covered'] . "</td>";
 241              $str .= '<td class="spikeDataTableCellCenter">' . $fileCoverage['uncovered'] . "</td>";
 242              $str .= '<td class="spikeDataTableCellCenter">' . ($fileCoverage['covered']+$fileCoverage['uncovered']) . "</td>";
 243              if($fileCoverage['uncovered'] + $fileCoverage['covered'] == 0) {
 244                  $str .= '<td class="spikeDataTableCellCenter">0%</td></tr>';
 245              }
 246              else {
 247                  $str .= '<td class="spikeDataTableCellCenter">' 
 248                  . round(($fileCoverage['covered']/($fileCoverage['uncovered'] 
 249                  + $fileCoverage['covered']))*100.0, 2)
 250                  . '%</td></tr>';
 251              }
 252              return $str;
 253          }
 254  
 255          /*}}}*/
 256          /*{{{ protected function writeIndexFileGrandTotalPercentage() */
 257  
 258          /** 
 259          * Writes the grand total for coverage recordings on the index.html 
 260          * 
 261          * @return string HTML code for grand total row
 262          * @access protected
 263          */
 264          protected function writeIndexFileGrandTotalPercentage() {
 265              $str = "";
 266  
 267              $str .= "<br/><h1>" . $this->heading . "</h1><br/>";
 268  
 269              $str .= '<table border="0" cellpadding="0" cellspacing="0" id="contentBox" width="800"> <tr>';
 270              $str .= '<td align="left" valign="top"><h1>Summary</h1>';
 271              $str .= '<table class="spikeVerticalTable" cellpadding="4" cellspacing="0" width="800" style="margin-bottom:10px" border="0">';
 272              $str .= '<td width="380" class="spikeVerticalTableHead" style="font-size:14px">Overall Code Coverage&nbsp;</td>';
 273              $str .= '<td class="spikeVerticalTableCell" style="font-size:14px" colspan="2"><strong>' . $this->getGrandCodeCoveragePercentage() . '%</td>';
 274  
 275              $str .= '</tr><tr>';
 276  
 277              $str .= '<td class="spikeVerticalTableHead">Total Covered Lines of Code&nbsp;</td>';
 278              $str .= '<td width="30" class="spikeVerticalTableCell"><span class="emphasis">' . $this->grandTotalCoveredLines.'</span></td>';
 279              $str .= '<td class="spikeVerticalTableCell"><span class="note">(' . TOTAL_COVERED_LINES_EXPLAIN . ')</span></td>';
 280  
 281              $str .= '</tr><tr>';
 282  
 283              $str .= '<td class="spikeVerticalTableHead">Total Missed Lines of Code&nbsp;</td>';
 284              $str .= '<td class="spikeVerticalTableCell"><span class="emphasis">' . $this->grandTotalUncoveredLines.'</span></td>';
 285              $str .= '<td class="spikeVerticalTableCell"><span class="note">(' . TOTAL_UNCOVERED_LINES_EXPLAIN . ')</span></td>';
 286  
 287              $str .= '</tr><tr>';
 288  
 289              $str .= '<td class="spikeVerticalTableHead">Total Lines of Code&nbsp;</td>';
 290              $str .= '<td class="spikeVerticalTableCell"><span class="emphasis">' . ($this->grandTotalCoveredLines + $this->grandTotalUncoveredLines) .'</span></td>';
 291              $str .= '<td class="spikeVerticalTableCell"><span class="note">(' . 
 292              TOTAL_LINES_OF_CODE_EXPLAIN . ')</span></td>';
 293  
 294              $str .= '</tr><tr>';
 295  
 296              $str .= '<td class="spikeVerticalTableHead" >Total Lines&nbsp;</td>';
 297              $str .= '<td class="spikeVerticalTableCell"><span class="emphasis">' . $this->grandTotalLines.'</span></td>';
 298              $str .= '<td class="spikeVerticalTableCell"><span class="note">(' . TOTAL_LINES_EXPLAIN . ')</span></td>';
 299  
 300              $str .= '</tr><tr>';
 301  
 302              $str .= '<td class="spikeVerticalTableHeadLast" >Total Files&nbsp;</td>';
 303              $str .= '<td class="spikeVerticalTableCellLast"><span class="emphasis">' . $this->grandTotalFiles.'</span></td>';
 304              $str .= '<td class="spikeVerticalTableCellLast"><span class="note">(' . TOTAL_FILES_EXPLAIN . ')</span></td>';
 305  
 306              $str .= '</tr></table>';
 307  
 308              return $str;
 309          }
 310  
 311          /*}}}*/
 312          /*{{{ protected function writeIndexFile() */
 313  
 314          /** 
 315          * Writes index.html file from all coverage recordings. 
 316          * 
 317          * @return boolean FALSE on failure
 318          * @access protected
 319          */
 320          protected function writeIndexFile() {
 321              global $util;
 322              $str = "";
 323              $this->createJSDir();
 324              $this->createImagesDir();
 325              $this->createStyleDir();
 326              $this->htmlFile = $this->outputDir . "/index.html";
 327              $indexFile = fopen($this->htmlFile, "w");
 328              if(empty($indexFile)) {
 329                  $this->logger->error("Cannot open file for writing: $this->htmlFile",
 330                      __FILE__, __LINE__);
 331                  return false;
 332              }
 333  
 334              $strHead = $this->writeIndexFileHeader();
 335              if($strHead == false) {
 336                  return false;
 337              }
 338              $str .= $this->writeIndexFileTableHead();
 339              $str .= '<tbody>';
 340              if(!empty($this->coverageData)) {
 341                  foreach($this->coverageData as $filename => &$lines) {
 342                      $realFile = realpath($filename);
 343                      $fileLink = $this->outputDir . $util->unixifyPath($realFile). ".html";
 344                      $fileCoverage = $this->markFile($realFile, $fileLink, $lines);
 345                      if(empty($fileCoverage)) {
 346                          return false;
 347                      }
 348                      $this->recordFileCoverageInfo($fileCoverage);
 349                      $this->updateGrandTotals($fileCoverage);
 350  
 351                      $str .= $this->writeIndexFileTableRow($fileLink, $realFile, $fileCoverage);
 352                      unset($this->coverageData[$filename]);
 353                  }
 354              }
 355              $str .= '</tbody>';
 356              $str .= "</table></td></tr>";
 357  
 358              $str .= "<tr><td><p align=\"right\" class=\"content\">Report Generated On: " . $util->getTimeStamp() . "<br/>";
 359              $str .= "Generated using Spike PHPCoverage " . $this->recorder->getVersion() . "</p></td></tr></table>";
 360  
 361              // Get the summary
 362              $strSummary = $this->writeIndexFileGrandTotalPercentage();
 363  
 364              // Merge them - with summary on top
 365              $str = $strHead . $strSummary . $str;
 366  
 367              $str .= $this->writeIndexFileFooter();
 368              fwrite($indexFile, $str);
 369              fclose($indexFile);
 370              return TRUE;
 371          }
 372  
 373          /*}}}*/
 374          /*{{{ private function writePhpFileHeader() */
 375  
 376          /** 
 377          * Write the header for the source file with mark-up
 378          * 
 379          * @param $filename Name of the php file
 380          * @return string String containing the HTML for PHP file header
 381          * @access private
 382          */
 383          private function writePhpFileHeader($filename, $fileLink) {
 384              $fileLink = $this->makeRelative($fileLink);
 385              $str = false;
 386              $dir = realpath(dirname(__FILE__));
 387              if($dir !== false) {
 388                  $str = file_get_contents($dir . "/" . $this->header);
 389                  if($str == false) {
 390                      return $str;
 391                  }
 392                  $str = str_replace("%%filename%%", $filename, $str);
 393                  // Get the path to parent CSS directory
 394                  $relativeCssPath = $this->getRelativeOutputDirPath($fileLink);
 395                  $relativeCssPath .= "/css/" . $this->style;
 396                  $str = str_replace("%%style%%", $relativeCssPath, $str);
 397              }
 398              return $str;
 399          }
 400  
 401          /*}}}*/
 402          /*{{{ private function writePhpFileFooter() */
 403  
 404          /** 
 405          * Write the footer for the source file with mark-up 
 406          * 
 407          * @return string String containing the HTML for PHP file footer
 408          * @access private
 409          */
 410          private function writePhpFileFooter() {
 411              $str = false;
 412              $dir = realpath(dirname(__FILE__));
 413              if($dir !== false) {
 414                  $str = file_get_contents($dir . "/" . $this->footer);
 415                  if($str == false) {
 416                      return $str;
 417                  }
 418              }
 419              return $str;
 420          }
 421  
 422          /*}}}*/
 423          /*{{{ protected function markFile() */
 424  
 425          /** 
 426          * Mark a source code file based on the coverage data gathered
 427          * 
 428          * @param $phpFile Name of the actual source file
 429          * @param $fileLink Link to the html mark-up file for the $phpFile
 430          * @param &$coverageLines Coverage recording for $phpFile
 431          * @return boolean FALSE on failure
 432          * @access protected
 433          */
 434          protected function markFile($phpFile, $fileLink, &$coverageLines) {
 435              global $util;
 436              $fileLink = $util->replaceBackslashes($fileLink);
 437              $parentDir = $util->replaceBackslashes(dirname($fileLink));
 438              if(!file_exists($parentDir)) {
 439                  //echo "\nCreating dir: $parentDir\n";
 440                  $util->makeDirRecursive($parentDir, 0755);
 441              }
 442              $writer = fopen($fileLink, "w");
 443  
 444              if(empty($writer)) {
 445                  $this->logger->error("Could not open file for writing: $fileLink",
 446                      __FILE__, __LINE__);
 447                  return false;
 448              }
 449  
 450              // Get the header for file
 451              $filestr = $this->writePhpFileHeader(basename($phpFile), $fileLink);
 452  
 453              // Add header for table
 454              $filestr .= '<table width="100%" border="0" cellpadding="2" cellspacing="0">';
 455              $filestr .= $this->writeFileTableHead();
 456  
 457              $lineCnt = $coveredCnt = $uncoveredCnt = 0;
 458              $parser = new PHPParser();
 459              $parser->parse($phpFile);
 460              $lastLineType = "non-exec";
 461