MDL-19579 code coverage - add Spike PHPCoverage lib (LGPL)
This commit is contained in:
@@ -0,0 +1,309 @@
|
||||
<?php
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* Copyright(c) 2004-2006, SpikeSource Inc. All Rights Reserved.
|
||||
* Licensed under the Open Software License version 2.1
|
||||
* (See http://www.spikesource.com/license.html)
|
||||
*/
|
||||
?>
|
||||
<?php
|
||||
|
||||
if(!defined("__PHPCOVERAGE_HOME")) {
|
||||
define("__PHPCOVERAGE_HOME", dirname(dirname(__FILE__)));
|
||||
}
|
||||
require_once __PHPCOVERAGE_HOME . "/util/Utility.php";
|
||||
require_once __PHPCOVERAGE_HOME . "/CoverageRecorder.php";
|
||||
require_once __PHPCOVERAGE_HOME . "/remote/XdebugTraceReader.php";
|
||||
require_once __PHPCOVERAGE_HOME . "/parser/CoverageXmlParser.php";
|
||||
|
||||
/**
|
||||
* A Coverage recorder extension for remote Coverage measurement.
|
||||
*
|
||||
* @author Nimish Pachapurkar <[email protected]>
|
||||
* @version $Revision$
|
||||
* @package SpikePHPCoverage_Remote
|
||||
*/
|
||||
class RemoteCoverageRecorder extends CoverageRecorder {
|
||||
/*{{{ Members */
|
||||
|
||||
protected $traceFilePath;
|
||||
protected $xdebugTraceReader;
|
||||
protected $tmpDir;
|
||||
protected $tmpTraceFilename = "phpcoverage.xdebug.trace";
|
||||
protected $coverageFileName = "phpcoverage.coverage.xml";
|
||||
|
||||
protected $xmlStart = "<?xml version=\"1.0\" encoding=\"utf-8\" ?><spike-phpcoverage>";
|
||||
protected $xmlEnd = "</spike-phpcoverage>";
|
||||
|
||||
/*}}}*/
|
||||
/*{{{ public function __construct() */
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function __construct(
|
||||
$includePaths=array("."),
|
||||
$excludePaths=array(),
|
||||
$reporter="new HtmlCoverageReporter()"
|
||||
) {
|
||||
global $util;
|
||||
parent::__construct($includePaths, $excludePaths, $reporter);
|
||||
$this->isRemote = true;
|
||||
$this->phpCoverageFiles[] = "phpcoverage.remote.inc.php";
|
||||
$this->phpCoverageFiles[] = "phpcoverage.remote.top.inc.php";
|
||||
$this->phpCoverageFiles[] = "phpcoverage.remote.bottom.inc.php";
|
||||
|
||||
// configuration
|
||||
$this->tmpDir = $util->getTmpDir();
|
||||
}
|
||||
|
||||
/*}}}*/
|
||||
/*{{{ Getters and Setters */
|
||||
|
||||
public function getTraceFilePath() {
|
||||
return $this->traceFilePath;
|
||||
}
|
||||
|
||||
public function setTraceFilePath($traceFilePath) {
|
||||
$this->traceFilePath = $traceFilePath;
|
||||
}
|
||||
|
||||
public function getTmpDir() {
|
||||
return $this->tmpDir;
|
||||
}
|
||||
|
||||
public function setTmpDir($tmpTraceDir) {
|
||||
$this->tmpDir = $tmpTraceDir;
|
||||
}
|
||||
|
||||
public function getCoverageFileName() {
|
||||
return $this->coverageFileName;
|
||||
}
|
||||
|
||||
public function setCoverageFileName($covFileName) {
|
||||
$this->coverageFileName = $covFileName;
|
||||
}
|
||||
|
||||
/*}}}*/
|
||||
/*{{{ public function cleanCoverageFile() */
|
||||
|
||||
/**
|
||||
* Deletes a coverage data file if one exists.
|
||||
*
|
||||
* @return Boolean True on success, False on failure.
|
||||
* @access public
|
||||
*/
|
||||
public function cleanCoverageFile() {
|
||||
$filepath = $this->tmpDir . "/" . $this->coverageFileName;
|
||||
if(file_exists($filepath)) {
|
||||
if(is_writable($filepath)) {
|
||||
unlink($filepath);
|
||||
}
|
||||
else {
|
||||
$this->logger->error("[RemoteCoverageRecorder::cleanCoverageFile()] "
|
||||
. "ERROR: Cannot delete $filepath.", __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*}}}*/
|
||||
/*{{{ protected function prepareCoverageXml() */
|
||||
|
||||
/**
|
||||
* Convert the Coverage data into an XML.
|
||||
*
|
||||
* @return String XML generated from Coverage data
|
||||
* @access protected
|
||||
*/
|
||||
protected function prepareCoverageXml() {
|
||||
global $util;
|
||||
$xmlString = "";
|
||||
$xmlBody = "";
|
||||
if(!empty($this->coverageData)) {
|
||||
foreach($this->coverageData as $file => &$lines) {
|
||||
$xmlBody .= "<file path=\"". $util->replaceBackslashes($file) . "\">";
|
||||
foreach($lines as $linenum => &$frequency) {
|
||||
$xmlBody .= "<line line-number=\"" . $linenum . "\"";
|
||||
$xmlBody .= " frequency=\"" . $frequency . "\"/>";
|
||||
}
|
||||
$xmlBody .= "</file>\n";
|
||||
}
|
||||
unset($this->coverageData);
|
||||
}
|
||||
else {
|
||||
$this->logger->info("[RemoteCoverageRecorder::prepareCoverageXml()] Coverage data is empty.",
|
||||
__FILE__, __LINE__);
|
||||
}
|
||||
$xmlString .= $xmlBody;
|
||||
$this->logger->debug("[RemoteCoverageRecorder::prepareCoverageXml()] Xml: " . $xmlString, __FILE__, __LINE__);
|
||||
return $xmlString;
|
||||
}
|
||||
|
||||
/*}}}*/
|
||||
/*{{{ protected function parseCoverageXml() */
|
||||
|
||||
/**
|
||||
* Parse coverage XML to regenerate the Coverage data array.
|
||||
*
|
||||
* @param $xml XML String or URL of the coverage data
|
||||
* @param $stream=false Is the input a stream?
|
||||
* @return
|
||||
* @access protected
|
||||
*/
|
||||
protected function parseCoverageXml(&$xml, $stream=false) {
|
||||
// Need to handle multiple xml files.
|
||||
if(!is_array($xml)) {
|
||||
$xml = array($xml);
|
||||
}
|
||||
for($i = 0; $i < count($xml); $i++) {
|
||||
$xmlParser = new CoverageXmlParser();
|
||||
if($stream) {
|
||||
$xmlParser->setInput($xml[$i]);
|
||||
}
|
||||
else {
|
||||
$xmlParser->setInputString($xml[$i]);
|
||||
}
|
||||
$xmlParser->parse();
|
||||
$data =& $xmlParser->getCoverageData();
|
||||
if(empty($this->coverageData)) {
|
||||
$this->coverageData = $data;
|
||||
}
|
||||
else {
|
||||
$data2 = array_merge_recursive($this->coverageData, $data);
|
||||
$this->coverageData = $data2;
|
||||
}
|
||||
$this->logger->debug("[RemoteCoverageRecorder::prepareCoverageXml()] " . "Coverage data intermediate: " . print_r($this->coverageData, true));
|
||||
}
|
||||
}
|
||||
|
||||
/*}}}*/
|
||||
/*{{{ public function getCoverageXml() */
|
||||
|
||||
/**
|
||||
* Dumps the coverage data in XML format
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function getCoverageXml() {
|
||||
$filepath = $this->tmpDir . "/" . $this->coverageFileName;
|
||||
if(file_exists($filepath) && is_readable($filepath)) {
|
||||
$fp = fopen($filepath, "r");
|
||||
if($fp) {
|
||||
while(!feof($fp)) {
|
||||
$xml = fread($fp, 4096);
|
||||
echo $xml;
|
||||
}
|
||||
fclose($fp);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
$this->logger->error("Could not read coverage data file.",
|
||||
__FILE__, __LINE__);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->logger->error("[RemoteCoverageRecorder::getCoverageXml()] "
|
||||
. "ERROR: Cannot read file " . $filepath, __FILE__, __LINE__);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*}}} */
|
||||
/*{{{ protected function appendDataToFile() */
|
||||
|
||||
/**
|
||||
* Append coverage data to xml file
|
||||
*
|
||||
* @param $newXml New xml recorded
|
||||
* @return True on success; false otherwise
|
||||
* @access protected
|
||||
*/
|
||||
protected function appendDataToFile($newXml) {
|
||||
$filepath = $this->tmpDir . "/" . $this->coverageFileName;
|
||||
if(!file_exists($filepath)) {
|
||||
// If new file, write the xml start and end tags
|
||||
$bytes = file_put_contents($filepath, $this->xmlStart . "\n" . $this->xmlEnd);
|
||||
if(!$bytes) {
|
||||
$this->logger->critical("[RemoteCoverageRecorder::appendDataToFile()] Could not create file: " . $filepath, __FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if(file_exists($filepath) && is_readable($filepath)) {
|
||||
$res = fopen($filepath, "r+");
|
||||
if($res) {
|
||||
fseek($res, -1 * strlen($this->xmlEnd), SEEK_END);
|
||||
$ret = fwrite($res, $newXml);
|
||||
if(!$ret) {
|
||||
$this->logger->error("[RemoteCoverageRecorder::appendDataToFile()] Could not append data to file.",
|
||||
__FILE__, __LINE__);
|
||||
fclose($res);
|
||||
return false;
|
||||
}
|
||||
fwrite($res, $this->xmlEnd);
|
||||
fclose($res);
|
||||
}
|
||||
else {
|
||||
$this->logger->error("[RemoteCoverageRecorder::appendDataToFile()] Error opening file for writing: " . $filepath,
|
||||
__FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*}}}*/
|
||||
/*{{{ public function saveCoverageXml() */
|
||||
|
||||
/**
|
||||
* Append coverage xml to a xml data file.
|
||||
*
|
||||
* @return Boolean True on success, False on error
|
||||
* @access public
|
||||
*/
|
||||
public function saveCoverageXml() {
|
||||
$filepath = $this->tmpDir . "/" . $this->coverageFileName;
|
||||
if($this->stopInstrumentation()) {
|
||||
$xml = $this->prepareCoverageXml();
|
||||
$ret = $this->appendDataToFile($xml);
|
||||
if(!$ret) {
|
||||
$this->logger->warn("[RemoteCoverageRecorder::saveCoverageXml()] "
|
||||
. "ERROR: Nothing was written to " . $filepath,
|
||||
__FILE__, __LINE__);
|
||||
return false;
|
||||
}
|
||||
$this->logger->info("[RemoteCoverageRecorder::saveCoverageXml()] "
|
||||
. "Saved XML to $filepath; size: [" . filesize($filepath)
|
||||
. "]", __FILE__, __LINE__);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*}}}*/
|
||||
/*{{{ public function generateReport() */
|
||||
|
||||
/**
|
||||
* Generate report from the xml coverage data
|
||||
* The preferred method for usage of this function is
|
||||
* passing a stream of the XML data in. This is much more
|
||||
* efficient and consumes less memory.
|
||||
*
|
||||
* @param $xmlUrl Url where XML data is available or string
|
||||
* @param $stream=false Is the xml available as stream?
|
||||
* @access public
|
||||
*/
|
||||
public function generateReport($xmlUrl, $stream=false) {
|
||||
$this->logger->debug("XML Url: " . $xmlUrl, __FILE__, __LINE__);
|
||||
$this->parseCoverageXml($xmlUrl, true);
|
||||
$this->logger->debug("Coverage Data final: " . print_r($this->coverageData, true));
|
||||
parent::generateReport();
|
||||
}
|
||||
|
||||
/*}}}*/
|
||||
}
|
||||
?>
|
||||
Reference in New Issue
Block a user