[ Index ]

PHP Cross Reference of Limb3

title

Body

[close]

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

   1  <?php
   2      /*
   3      *  $Id: CoverageRecorder.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(__FILE__));
  14      }
  15      require_once  __PHPCOVERAGE_HOME . "/conf/phpcoverage.conf.php";
  16      require_once  __PHPCOVERAGE_HOME . "/util/Utility.php";
  17      require_once  __PHPCOVERAGE_HOME . "/reporter/CoverageReporter.php";
  18  
  19      /**
  20      *
  21      * The Coverage Recorder utility
  22      *
  23      * This is the main class for the CoverageRecorder. User should
  24      * instantiate this class and set various parameters of it.
  25      * The startInstrumentation and stopInstrumentation methods will
  26      * switch code coverage recording on and off respectively.
  27      *
  28      * The code coverage is recorded using XDebug Zend Extension. Therefore,
  29      * it is required to install that extension on the system where
  30      * code coverage measurement is going to take place. See
  31      * {@link http://www.xdebug.org www.xdebug.org} for more
  32      * information.
  33      *
  34      * @author        Nimish Pachapurkar <npac@spikesource.com>
  35      * @version        $Revision: 14665 $
  36      */
  37      class CoverageRecorder {
  38  
  39          // {{{ Members
  40  
  41          protected $includePaths;
  42          protected $excludePaths;
  43          protected $reporter;
  44          protected $coverageData;
  45          protected $isRemote = false;
  46          protected $stripped = false;
  47          protected $phpCoverageFiles = array("phpcoverage.inc.php");
  48          protected $version;
  49          protected $logger;
  50  
  51          /**
  52          * What extensions are treated as php files.
  53          *
  54          * @param "php" Array of extension strings
  55          */
  56          protected $phpExtensions;
  57  
  58          // }}}
  59          // {{{ Constructor
  60  
  61          /**
  62          * Constructor (PHP5 only)
  63          *
  64          * @param $includePaths Directories to be included in code coverage report
  65          * @param $excludePaths Directories to be excluded from code coverage report
  66          * @param $reporter Instance of a Reporter subclass
  67          * @access public
  68          */
  69          public function __construct(
  70              $includePaths=array("."),
  71              $excludePaths=array(),
  72              $reporter="new HtmlCoverageReporter()"
  73          ) {
  74  
  75              $this->includePaths = $includePaths;
  76              $this->excludePaths = $excludePaths;
  77              $this->reporter = $reporter;
  78              // Set back reference
  79              $this->reporter->setCoverageRecorder($this);
  80              $this->excludeCoverageDir();
  81              $this->version = "0.6.6";
  82  
  83              // Configuration
  84              global $spc_config;
  85              $this->phpExtensions = $spc_config['extensions'];
  86              global $util;
  87              $this->logger = $util->getLogger();
  88          }
  89  
  90          // }}}
  91          // {{{ public function startInstrumentation()
  92  
  93          /**
  94          * Starts the code coverage recording
  95          *
  96          * @access public
  97          */
  98          public function startInstrumentation() {
  99              if(extension_loaded("xdebug")) {
 100                  xdebug_start_code_coverage();
 101                  return true;
 102              }
 103              $this->logger->critical("[CoverageRecorder::startInstrumentation()] "
 104              . "ERROR: Xdebug not loaded.", __FILE__, __LINE__);
 105              return false;
 106          }
 107  
 108          // }}}
 109          // {{{ public function stopInstrumentation()
 110  
 111          /**
 112          * Stops code coverage recording
 113          *
 114          * @access public
 115          */
 116          public function stopInstrumentation() {
 117              if(extension_loaded("xdebug")) {
 118                  $this->coverageData = xdebug_get_code_coverage();
 119                  foreach($this->coverageData as $file => $data) {
 120                         if (!file_exists($file)) {
 121                                 unset($this->coverageData[$file]);
 122                         }
 123                  }
 124  
 125          xdebug_stop_code_coverage();
 126                  $this->logger->debug("[CoverageRecorder::stopInstrumentation()] Code coverage: " . print_r($this->coverageData, true),
 127                      __FILE__, __LINE__);
 128                  return true;
 129              }
 130              return false;
 131          }
 132  
 133          // }}}
 134          // {{{ public function generateReport()
 135  
 136          /**
 137          * Generate the code coverage report
 138          *
 139          * @access public
 140          */
 141          public function generateReport() {
 142              if($this->isRemote) {
 143                  $this->logger->info("[CoverageRecorder::generateReport()] "
 144                  ."Writing report.", __FILE__, __LINE__);
 145              }
 146              else {
 147                  $this->logger->info("[CoverageRecorder::generateReport()] "
 148                  . "Writing report:\t\t", __FILE__, __LINE__);
 149              }
 150              $this->logger->debug("[CoverageRecoder::generateReport()] " . print_r($this->coverageData, true),
 151                  __FILE__, __LINE__);
 152              $this->unixifyCoverageData();
 153              $this->coverageData = $this->stripCoverageData();
 154              $this->reporter->generateReport($this->coverageData);
 155              if($this->isRemote) {
 156                  $this->logger->info("[CoverageRecorder::generateReport()] [done]", __FILE__, __LINE__);
 157              }
 158              else {
 159                  $this->logger->info("[done]", __FILE__, __LINE__);
 160              }
 161          }
 162  
 163          // }}}
 164          /*{{{ protected function removeAbsentPaths() */
 165  
 166          /**
 167          * Remove the directories that do not exist from the input array
 168          *
 169          * @param &$dirs Array of directory names
 170          * @access protected
 171          */
 172          protected function removeAbsentPaths(&$dirs) {
 173              for($i = 0; $i < count($dirs); $i++) {
 174                  if(! file_exists($dirs[$i])) {
 175                      // echo "Not found: " . $dirs[$i] . "\n";
 176                      $this->errors[] = "Not found: " . $dirs[$i]
 177                      . ". Removing ...";
 178                      array_splice($dirs, $i, 1);
 179                      $i--;
 180                  }
 181                  else {
 182                      $dirs[$i] = realpath($dirs[$i]);
 183                  }
 184              }
 185          }
 186  
 187          /*}}}*/
 188          // {{{ protected function processSourcePaths()
 189  
 190          /**
 191          * Processes and validates the source directories
 192          *
 193          * @access protected
 194          */
 195          protected function processSourcePaths() {
 196              $this->removeAbsentPaths($this->includePaths);
 197              $this->removeAbsentPaths($this->excludePaths);
 198  
 199              sort($this->includePaths, SORT_STRING);
 200          }
 201  
 202          // }}}
 203          /*{{{ protected function getFilesAndDirs() */
 204  
 205          /**
 206          * Get the list of files that match the extensions in $this->phpExtensions
 207          *
 208          * @param $dir Root directory
 209          * @param &$files Array of filenames to append to
 210          * @access protected
 211          */
 212          protected function getFilesAndDirs($dir, &$files) {
 213              global $util;
 214              $dirs[] = $dir;
 215              while(count($dirs) > 0) {
 216                  $currDir = realpath(array_pop($dirs));
 217                  if(!is_readable($currDir)) {
 218                      continue;
 219                  }
 220                  //echo "Current Dir: $currDir \n";
 221                  $currFiles = scandir($currDir);
 222                  //print_r($currFiles);
 223                  for($j = 0; $j < count($currFiles); $j++) {
 224                      if($currFiles[$j] == "." || $currFiles[$j] == "..") {
 225                          continue;
 226                      }
 227                      $currFiles[$j] = $currDir . "/" . $currFiles[$j];
 228                      //echo "Current File: " . $currFiles[$j] . "\n";
 229                      if(is_file($currFiles[$j])) {
 230                          $pathParts = pathinfo($currFiles[$j]);
 231                          if(isset($pathParts['extension']) && in_array($pathParts['extension'], $this->phpExtensions)) {
 232                              $files[] = $util->replaceBackslashes($currFiles[$j]);
 233                          }
 234                      }
 235                      if(is_dir($currFiles[$j])) {
 236                          $dirs[] = $currFiles[$j];
 237                      }
 238                  }
 239              }
 240          }
 241  
 242          /*}}}*/
 243          /*{{{ protected function addFiles() */
 244  
 245          /**
 246          * Add all source files to the list of files that need to be parsed.
 247          *
 248          * @access protected
 249          */
 250          protected function addFiles() {
 251              global $util;
 252              $files = array();
 253              for($i = 0; $i < count($this->includePaths); $i++) {
 254                  $this->includePaths[$i] = $util->replaceBackslashes($this->includePaths[$i]);
 255                  if(is_dir($this->includePaths[$i])) {
 256                      //echo "Calling getFilesAndDirs with " . $this->includePaths[$i] . "\n";
 257                      $this->getFilesAndDirs($this->includePaths[$i], $files);
 258                  }
 259                  else if(is_file($this->includePaths[$i])) {
 260                      $files[] = $this->includePaths[$i];
 261                  }
 262              }
 263  
 264              $this->logger->debug("Found files:" . print_r($files, true),
 265                  __FILE__, __LINE__);
 266              for($i = 0; $i < count($this->excludePaths); $i++) {
 267                  $this->excludePaths[$i] = $util->replaceBackslashes($this->excludePaths[$i]);
 268              }
 269  
 270              for($i = 0; $i < count($files); $i++) {
 271                  for($j = 0; $j < count($this->excludePaths); $j++) {
 272                      $this->logger->debug($files[$i] . "\t" . $this->excludePaths[$j] . "\n", __FILE__, __LINE__);
 273                      if(strpos($files[$i], $this->excludePaths[$j]) === 0) {
 274                          continue;
 275                      }
 276                  }
 277                  if(!array_key_exists($files[$i], $this->coverageData)) {
 278                      $this->coverageData[$files[$i]] =  array();
 279                  }
 280              }
 281          }
 282  
 283          /*}}}*/
 284          // {{{ protected function stripCoverageData()
 285  
 286          /**
 287          * Removes the unwanted coverage data from the recordings
 288          *
 289          * @return Processed coverage data
 290          * @access protected
 291          */
 292          protected function stripCoverageData() {
 293              if($this->stripped) {
 294                  $this->logger->debug("[CoverageRecorder::stripCoverageData()] Already stripped!", __FILE__, __LINE__);
 295                  return $this->coverageData;
 296              }
 297              $this->stripped = true;
 298              if(empty($this->coverageData)) {
 299                  $this->logger->warn("[CoverageRecorder::stripCoverageData()] No coverage data found.", __FILE__, __LINE__);
 300                  return $this->coverageData;
 301              }
 302              $this->processSourcePaths();
 303              $this->logger->debug("!!!!!!!!!!!!! Source Paths !!!!!!!!!!!!!!",
 304                  __FILE__, __LINE__);
 305              $this->logger->debug(print_r($this->includePaths, true),
 306                  __FILE__, __LINE__);
 307              $this->logger->debug(print_r($this->excludePaths, true),
 308                  __FILE__, __LINE__);
 309              $this->logger->debug("!!!!!!!!!!!!! Source Paths !!!!!!!!!!!!!!",
 310                  __FILE__, __LINE__);
 311              $this->addFiles();
 312              $altCoverageData = array();
 313              foreach ($this->coverageData as $filename => &$lines) {
 314                  $preserve = false;
 315                  $realFile = $filename;
 316                  for($i = 0; $i < count($this->includePaths); $i++) {
 317                      if(strpos($realFile, $this->includePaths[$i]) === 0) {
 318                          $preserve = true;
 319                      }
 320                      else {
 321                          $this->logger->debug("File: " . $realFile
 322                          . "\nDoes not match: " . $this->includePaths[$i],
 323                          __FILE__, __LINE__);
 324                      }
 325                  }
 326                  // Exclude dirs have a precedence over includes.
 327                  for($i = 0; $i < count($this->excludePaths); $i++) {
 328                      if(strpos($realFile, $this->excludePaths[$i]) === 0) {
 329                          $preserve = false;
 330                      }
 331                      else if(in_array(basename($realFile), $this->phpCoverageFiles)) {
 332                          $preserve = false;
 333                      }
 334                  }
 335                  if($preserve) {
 336                      // Should be preserved
 337                      $altCoverageData[$filename] = $lines;
 338                  }
 339              }
 340  
 341              array_multisort($altCoverageData, SORT_STRING);
 342              return $altCoverageData;
 343          }
 344  
 345          // }}}
 346          /*{{{ protected function unixifyCoverageData() */
 347  
 348          /**
 349          * Convert filepaths in coverage data to forward slash separated
 350          * paths.
 351          *
 352          * @access protected
 353          */
 354          protected function unixifyCoverageData() {
 355              global $util;
 356              $tmpCoverageData = array();
 357              foreach($this->coverageData as $file => &$lines) {
 358                  $tmpCoverageData[$util->replaceBackslashes(realpath($file))] = $lines;
 359              }
 360              $this->coverageData = $tmpCoverageData;
 361          }
 362  
 363          /*}}}*/
 364          // {{{ public function getErrors()
 365  
 366          /**
 367          * Returns the errors array containing all error encountered so far.
 368          *
 369          * @return Array of error messages
 370          * @access public
 371          */
 372          public function getErrors() {
 373              return $this->errors;
 374          }
 375  
 376          // }}}
 377          // {{{ public function logErrors()
 378  
 379          /**
 380          * Writes all error messages to error log
 381          *
 382          * @access public
 383          */
 384          public function logErrors() {
 385              $this->logger->error(print_r($this->errors, true),
 386                  __FILE__, __LINE__);
 387          }
 388  
 389          // }}}
 390          /*{{{ Getters and Setters */
 391  
 392          public function getIncludePaths() {
 393              return $this->includePaths;
 394          }
 395  
 396          public function setIncludePaths($includePaths) {
 397              $this->includePaths = $includePaths;
 398          }
 399  
 400          public function getExcludePaths() {
 401              return $this->excludePaths;
 402          }
 403  
 404          public function setExcludePaths($excludePaths) {
 405              $this->excludePaths = $excludePaths;
 406              $this->excludeCoverageDir();
 407          }
 408  
 409          public function getReporter() {
 410              return $this->reporter;
 411          }
 412  
 413          public function setReporter(&$reporter) {
 414              $this->reporter = $reporter;
 415          }
 416  
 417          public function getPhpExtensions() {
 418              return $this->phpExtensions;
 419          }
 420  
 421          public function setPhpExtensions(&$extensions) {
 422              $this->phpExtensions = $extensions;
 423          }
 424  
 425          public function getVersion() {
 426              return $this->version;
 427          }
 428  
 429          /*}}}*/
 430          /*{{{ public function excludeCoverageDir() */
 431  
 432          /**
 433          * Exclude the directory containing the coverage measurement code.
 434          *
 435          * @access public
 436          */
 437          public function excludeCoverageDir() {
 438              $f = __FILE__;
 439              if(is_link($f)) {
 440                  $f = readlink($f);
 441              }
 442              $this->excludePaths[] = realpath(dirname($f));
 443          }
 444          /*}}}*/
 445      }
 446  ?>