|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00002 /* 00003 * $Id: CoverageRecorder.php,v 1.2 2010/12/14 17:35:58 moodlerobot Exp $ 00004 * 00005 * Copyright(c) 2004-2006, SpikeSource Inc. All Rights Reserved. 00006 * Licensed under the Open Software License version 2.1 00007 * (See http://www.spikesource.com/license.html) 00008 */ 00009 ?> 00010 <?php 00011 00012 if(!defined("__PHPCOVERAGE_HOME")) { 00013 define("__PHPCOVERAGE_HOME", dirname(__FILE__)); 00014 } 00015 require_once __PHPCOVERAGE_HOME . "/conf/phpcoverage.conf.php"; 00016 require_once __PHPCOVERAGE_HOME . "/util/Utility.php"; 00017 require_once __PHPCOVERAGE_HOME . "/reporter/CoverageReporter.php"; 00018 00037 class CoverageRecorder { 00038 00039 // {{{ Members 00040 00041 protected $includePaths; 00042 protected $excludePaths; 00043 protected $reporter; 00044 protected $coverageData; 00045 protected $isRemote = false; 00046 protected $stripped = false; 00047 protected $phpCoverageFiles = array("phpcoverage.inc.php"); 00048 protected $version; 00049 protected $logger; 00050 00056 protected $phpExtensions; 00057 00058 // }}} 00059 // {{{ Constructor 00060 00069 public function __construct( 00070 $includePaths=array("."), 00071 $excludePaths=array(), 00072 $reporter="new HtmlCoverageReporter()" 00073 ) { 00074 00075 $this->includePaths = $includePaths; 00076 $this->excludePaths = $excludePaths; 00077 $this->reporter = $reporter; 00078 // Set back reference 00079 $this->reporter->setCoverageRecorder($this); 00080 $this->excludeCoverageDir(); 00081 $this->version = "0.8.2"; 00082 00083 // Configuration 00084 global $spc_config; 00085 $this->phpExtensions = $spc_config['extensions']; 00086 global $util; 00087 $this->logger = $util->getLogger(); 00088 } 00089 00090 // }}} 00091 // {{{ public function startInstrumentation() 00092 00098 public function startInstrumentation() { 00099 if(extension_loaded("xdebug")) { 00100 xdebug_start_code_coverage(); 00101 return true; 00102 } 00103 $this->logger->critical("[CoverageRecorder::startInstrumentation()] " 00104 . "ERROR: Xdebug not loaded.", __FILE__, __LINE__); 00105 return false; 00106 } 00107 00108 // }}} 00109 // {{{ public function stopInstrumentation() 00110 00116 public function stopInstrumentation() { 00117 if(extension_loaded("xdebug")) { 00118 $this->coverageData = xdebug_get_code_coverage(); 00119 xdebug_stop_code_coverage(); 00120 $this->logger->debug("[CoverageRecorder::stopInstrumentation()] Code coverage: " . print_r($this->coverageData, true), 00121 __FILE__, __LINE__); 00122 return true; 00123 } 00124 else { 00125 $this->logger->critical("[CoverageRecorder::stopInstrumentation()] Xdebug not loaded.", __FILE__, __LINE__); 00126 } 00127 return false; 00128 } 00129 00130 // }}} 00131 // {{{ public function generateReport() 00132 00138 public function generateReport() { 00139 if($this->isRemote) { 00140 $this->logger->info("[CoverageRecorder::generateReport()] " 00141 ."Writing report.", __FILE__, __LINE__); 00142 } 00143 else { 00144 $this->logger->info("[CoverageRecorder::generateReport()] " 00145 . "Writing report:\t\t", __FILE__, __LINE__); 00146 } 00147 $this->logger->debug("[CoverageRecoder::generateReport()] " . print_r($this->coverageData, true), 00148 __FILE__, __LINE__); 00149 $this->unixifyCoverageData(); 00150 $this->coverageData = $this->stripCoverageData(); 00151 $this->reporter->generateReport($this->coverageData); 00152 if($this->isRemote) { 00153 $this->logger->info("[CoverageRecorder::generateReport()] [done]", __FILE__, __LINE__); 00154 } 00155 else { 00156 $this->logger->info("[done]", __FILE__, __LINE__); 00157 } 00158 } 00159 00160 // }}} 00161 /*{{{ protected function removeAbsentPaths() */ 00162 00169 protected function removeAbsentPaths(&$dirs) { 00170 for($i = 0; $i < count($dirs); $i++) { 00171 if(! file_exists($dirs[$i])) { 00172 // echo "Not found: " . $dirs[$i] . "\n"; 00173 $this->errors[] = "Not found: " . $dirs[$i] 00174 . ". Removing ..."; 00175 array_splice($dirs, $i, 1); 00176 $i--; 00177 } 00178 else { 00179 $dirs[$i] = realpath($dirs[$i]); 00180 } 00181 } 00182 } 00183 00184 /*}}}*/ 00185 // {{{ protected function processSourcePaths() 00186 00192 protected function processSourcePaths() { 00193 $this->removeAbsentPaths($this->includePaths); 00194 $this->removeAbsentPaths($this->excludePaths); 00195 00196 sort($this->includePaths, SORT_STRING); 00197 } 00198 00199 // }}} 00200 /*{{{ protected function getFilesAndDirs() */ 00201 00209 protected function getFilesAndDirs($dir, &$files) { 00210 global $util; 00211 $dirs[] = $dir; 00212 while(count($dirs) > 0) { 00213 $currDir = realpath(array_pop($dirs)); 00214 if(!is_readable($currDir)) { 00215 continue; 00216 } 00217 //echo "Current Dir: $currDir \n"; 00218 $currFiles = scandir($currDir); 00219 //print_r($currFiles); 00220 for($j = 0; $j < count($currFiles); $j++) { 00221 if($currFiles[$j] == "." || $currFiles[$j] == "..") { 00222 continue; 00223 } 00224 $currFiles[$j] = $currDir . "/" . $currFiles[$j]; 00225 //echo "Current File: " . $currFiles[$j] . "\n"; 00226 if(is_file($currFiles[$j])) { 00227 $pathParts = pathinfo($currFiles[$j]); 00228 if(isset($pathParts['extension']) && in_array($pathParts['extension'], $this->phpExtensions)) { 00229 $files[] = $util->replaceBackslashes($currFiles[$j]); 00230 } 00231 } 00232 if(is_dir($currFiles[$j])) { 00233 $dirs[] = $currFiles[$j]; 00234 } 00235 } 00236 } 00237 } 00238 00239 /*}}}*/ 00240 /*{{{ protected function addFiles() */ 00241 00247 protected function addFiles() { 00248 global $util; 00249 $files = array(); 00250 for($i = 0; $i < count($this->includePaths); $i++) { 00251 $this->includePaths[$i] = $util->replaceBackslashes($this->includePaths[$i]); 00252 if(is_dir($this->includePaths[$i])) { 00253 //echo "Calling getFilesAndDirs with " . $this->includePaths[$i] . "\n"; 00254 $this->getFilesAndDirs($this->includePaths[$i], $files); 00255 } 00256 else if(is_file($this->includePaths[$i])) { 00257 $files[] = $this->includePaths[$i]; 00258 } 00259 } 00260 00261 $this->logger->debug("Found files:" . print_r($files, true), 00262 __FILE__, __LINE__); 00263 for($i = 0; $i < count($this->excludePaths); $i++) { 00264 $this->excludePaths[$i] = $util->replaceBackslashes($this->excludePaths[$i]); 00265 } 00266 00267 for($i = 0; $i < count($files); $i++) { 00268 for($j = 0; $j < count($this->excludePaths); $j++) { 00269 $this->logger->debug($files[$i] . "\t" . $this->excludePaths[$j] . "\n", __FILE__, __LINE__); 00270 if(strpos($files[$i], $this->excludePaths[$j]) === 0) { 00271 continue; 00272 } 00273 } 00274 if(!array_key_exists($files[$i], $this->coverageData)) { 00275 $this->coverageData[$files[$i]] = array(); 00276 } 00277 } 00278 } 00279 00280 /*}}}*/ 00281 // {{{ protected function stripCoverageData() 00282 00289 protected function stripCoverageData() { 00290 if($this->stripped) { 00291 $this->logger->debug("[CoverageRecorder::stripCoverageData()] Already stripped!", __FILE__, __LINE__); 00292 return $this->coverageData; 00293 } 00294 $this->stripped = true; 00295 if(empty($this->coverageData)) { 00296 $this->logger->warn("[CoverageRecorder::stripCoverageData()] No coverage data found.", __FILE__, __LINE__); 00297 return $this->coverageData; 00298 } 00299 $this->processSourcePaths(); 00300 $this->logger->debug("!!!!!!!!!!!!! Source Paths !!!!!!!!!!!!!!", 00301 __FILE__, __LINE__); 00302 $this->logger->debug(print_r($this->includePaths, true), 00303 __FILE__, __LINE__); 00304 $this->logger->debug(print_r($this->excludePaths, true), 00305 __FILE__, __LINE__); 00306 $this->logger->debug("!!!!!!!!!!!!! Source Paths !!!!!!!!!!!!!!", 00307 __FILE__, __LINE__); 00308 $this->addFiles(); 00309 $altCoverageData = array(); 00310 foreach ($this->coverageData as $filename => &$lines) { 00311 $preserve = false; 00312 $realFile = $filename; 00313 for($i = 0; $i < count($this->includePaths); $i++) { 00314 if(strpos($realFile, $this->includePaths[$i]) === 0) { 00315 $preserve = true; 00316 } 00317 else { 00318 $this->logger->debug("File: " . $realFile 00319 . "\nDoes not match: " . $this->includePaths[$i], 00320 __FILE__, __LINE__); 00321 } 00322 } 00323 // Exclude dirs have a precedence over includes. 00324 for($i = 0; $i < count($this->excludePaths); $i++) { 00325 if(strpos($realFile, $this->excludePaths[$i]) === 0) { 00326 $preserve = false; 00327 } 00328 else if(in_array(basename($realFile), $this->phpCoverageFiles)) { 00329 $preserve = false; 00330 } 00331 } 00332 if($preserve) { 00333 // Should be preserved 00334 $altCoverageData[$filename] = $lines; 00335 } 00336 00337 // Fix for bug #34 <http://developer.spikesource.com/tracker/index.php?func=detail&aid=34&group_id=9&atid=117> 00338 // Finally get rid of data for extensions we don't care about 00339 if (is_file($filename)) { 00340 $parts = pathinfo($filename); 00341 if (!empty($parts['extension']) && !in_array($parts['extension'], $this->phpExtensions)) { 00342 // Remove this key and its contents 00343 if (isset($altCoverageData[$filename])) { 00344 $this->logger->debug("!!! Extension mismatch; Removing file: " . $filename, __FILE__, __LINE__); 00345 unset($altCoverageData[$filename]); 00346 } 00347 } 00348 } 00349 } 00350 00351 array_multisort($altCoverageData, SORT_STRING); 00352 return $altCoverageData; 00353 } 00354 00355 // }}} 00356 /*{{{ protected function unixifyCoverageData() */ 00357 00364 protected function unixifyCoverageData() { 00365 global $util; 00366 $tmpCoverageData = array(); 00367 foreach($this->coverageData as $file => &$lines) { 00368 $tmpCoverageData[$util->replaceBackslashes(realpath($file))] = $lines; 00369 } 00370 $this->coverageData = $tmpCoverageData; 00371 } 00372 00373 /*}}}*/ 00374 // {{{ public function getErrors() 00375 00382 public function getErrors() { 00383 return $this->errors; 00384 } 00385 00386 // }}} 00387 // {{{ public function logErrors() 00388 00394 public function logErrors() { 00395 $this->logger->error(print_r($this->errors, true), 00396 __FILE__, __LINE__); 00397 } 00398 00399 // }}} 00400 /*{{{ Getters and Setters */ 00401 00402 public function getIncludePaths() { 00403 return $this->includePaths; 00404 } 00405 00406 public function setIncludePaths($includePaths) { 00407 $this->includePaths = $includePaths; 00408 } 00409 00410 public function getExcludePaths() { 00411 return $this->excludePaths; 00412 } 00413 00414 public function setExcludePaths($excludePaths) { 00415 $this->excludePaths = $excludePaths; 00416 $this->excludeCoverageDir(); 00417 } 00418 00419 public function getReporter() { 00420 return $this->reporter; 00421 } 00422 00423 public function setReporter(&$reporter) { 00424 $this->reporter = $reporter; 00425 } 00426 00427 public function getPhpExtensions() { 00428 return $this->phpExtensions; 00429 } 00430 00431 public function setPhpExtensions(&$extensions) { 00432 $this->phpExtensions = $extensions; 00433 } 00434 00435 public function getVersion() { 00436 return $this->version; 00437 } 00438 00439 /*}}}*/ 00440 /*{{{ public function excludeCoverageDir() */ 00441 00447 public function excludeCoverageDir() { 00448 $f = __FILE__; 00449 if(is_link($f)) { 00450 $f = readlink($f); 00451 } 00452 $this->excludePaths[] = realpath(dirname($f)); 00453 } 00454 /*}}}*/ 00455 } 00456 ?>