Created
April 12, 2013 16:08
-
-
Save satooshi/5373125 to your computer and use it in GitHub Desktop.
I wrote PHP library for Coveralls. https://github.com/satooshi/php-coveralls
PHP script for Coveralls. clover.xml generated by PHPUnit is required to get coverage data. And repo_token for Coveralls repository is required for API access.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
// data | |
/** | |
* Data represents "source_files" element of Coveralls' "json_file". | |
* | |
* @author Kitamura Satoshi <[email protected]> | |
*/ | |
class SourceFile | |
{ | |
/** | |
* Source filename. | |
* | |
* @var string | |
*/ | |
protected $name; | |
/** | |
* Source content. | |
* | |
* @var string | |
*/ | |
protected $source; | |
/** | |
* Coverage data of the source file. | |
* | |
* @var array | |
*/ | |
protected $coverage; | |
/** | |
* Absolute path. | |
* | |
* @var string | |
*/ | |
protected $path; | |
/** | |
* Line number of the source file. | |
* | |
* @var integer | |
*/ | |
protected $fileLines; | |
/** | |
* Constructor. | |
* | |
* @param string $path Absolute path. | |
* @param string $name Source filename. | |
* @param string $eol End of line. | |
*/ | |
public function __construct($path, $name, $eol = "\n") | |
{ | |
$this->path = $path; | |
$this->name = $name; | |
$this->source = file_get_contents($path); | |
$lines = explode($eol, $this->source); | |
$this->fileLines = count($lines); | |
$this->coverage = array_fill(0, $this->fileLines, null); | |
} | |
/** | |
* String expression (convert to json). | |
* | |
* @return string | |
*/ | |
public function __toString() | |
{ | |
return json_encode($this->toArray()); | |
} | |
/** | |
* Convert to an array. | |
* | |
* @return array | |
*/ | |
public function toArray() | |
{ | |
return array( | |
'name' => $this->name, | |
'source' => $this->source, | |
'coverage' => $this->coverage, | |
); | |
} | |
// API | |
/** | |
* Add coverage. | |
* | |
* @param integer $lineNum Line number. | |
* @param integer $count Number of covered. | |
* @return void | |
*/ | |
public function addCoverage($lineNum, $count) | |
{ | |
$this->coverage[$lineNum] = $count; | |
} | |
// accessor | |
/** | |
* Return source filename. | |
* | |
* @return string | |
*/ | |
public function getName() | |
{ | |
return $this->name; | |
} | |
/** | |
* Return source content. | |
* | |
* @return string | |
*/ | |
public function getSource() | |
{ | |
return $this->source; | |
} | |
/** | |
* Return coverage data of the source file. | |
* | |
* @return array | |
*/ | |
public function getCoverage() | |
{ | |
return $this->coverage; | |
} | |
/** | |
* Return absolute path. | |
* | |
* @return string | |
*/ | |
public function getPath() | |
{ | |
return $this->path; | |
} | |
/** | |
* Return line number of the source file. | |
* | |
* @return integer | |
*/ | |
public function getFileLines() | |
{ | |
return $this->fileLines; | |
} | |
} | |
/** | |
* Data represents "json_file" of Coveralls API. | |
* | |
* @author Kitamura Satoshi <[email protected]> | |
*/ | |
class Coveralls | |
{ | |
/** | |
* Service name. | |
* | |
* @var string | |
*/ | |
protected $serviceName; | |
/** | |
* Repository token. | |
* | |
* @var string | |
*/ | |
protected $repoToken; | |
/** | |
* Source files. | |
* | |
* @var SourceFile[] | |
*/ | |
protected $sourceFiles = array(); | |
/** | |
* Constructor. | |
* | |
* @param string $serviceName Service name. | |
* @param string $repoToken Repo token. | |
*/ | |
public function __construct($serviceName = 'travis-ci', $repoToken = null) | |
{ | |
$this->serviceName = $serviceName; | |
$this->repoToken = $repoToken; | |
} | |
/** | |
* String expression (convert to json). | |
* | |
* @return string | |
*/ | |
public function __toString() | |
{ | |
return json_encode($this->toArray()); | |
} | |
/** | |
* Convert to an array. | |
* | |
* @return array | |
*/ | |
public function toArray() | |
{ | |
$files = array(); | |
foreach ($this->sourceFiles as $file) { | |
$files[] = $file->toArray(); | |
} | |
$data = array( | |
'service_name' => $this->serviceName, | |
'source_files' => $files, | |
); | |
if ($this->repoToken !== null) { | |
$data['repo_token'] = $this->repoToken; | |
} | |
return $data; | |
} | |
// API | |
/** | |
* Add SourceFile. | |
* | |
* @param SourceFile $sourceFile | |
*/ | |
public function addSourceFile(SourceFile $sourceFile) | |
{ | |
$this->sourceFiles[] = $sourceFile; | |
} | |
/** | |
* Return whether the SourceFile object exists. | |
* | |
* @return boolean | |
*/ | |
public function hasSourceFiles() | |
{ | |
return count($this->sourceFiles) > 0; | |
} | |
// accessor | |
/** | |
* Set service name. | |
* | |
* @param string $serviceName Service name. | |
* @return Coveralls | |
*/ | |
public function setServiceName($serviceName) | |
{ | |
$this->serviceName = $serviceName; | |
return $this; | |
} | |
/** | |
* Return service name. | |
* | |
* @return string. | |
*/ | |
public function getServiceName() | |
{ | |
return $this->serviceName; | |
} | |
/** | |
* Set repository token. | |
* | |
* @param string $repoToken Repository token. | |
* @return Coveralls | |
*/ | |
public function setRepoToken($repoToken) | |
{ | |
$this->repoToken = $repoToken; | |
return $this; | |
} | |
/** | |
* Return repository token. | |
* | |
* @return string | |
*/ | |
public function getRepoToken() | |
{ | |
return $this->repoToken; | |
} | |
/** | |
* Return source files. | |
* | |
* @return SourceFile[] | |
*/ | |
public function getSourceFiles() | |
{ | |
return $this->sourceFiles; | |
} | |
} | |
// collector | |
/** | |
* Coverage collector for clover.xml. | |
* | |
* @author Kitamura Satoshi <[email protected]> | |
*/ | |
class CloverXmlCoverageCollector | |
{ | |
// API | |
/** | |
* Collect coverage from XML object. | |
* | |
* @param SimpleXMLElement $xml Clover XML object. | |
* @param string $root Path to src directory. | |
* @return Coveralls | |
*/ | |
public function collect(SimpleXMLElement $xml, $root) | |
{ | |
$coveralls = new Coveralls(); | |
foreach ($xml->project->package as $package) { | |
foreach ($package->file as $file) { | |
$srcFile = $this->collectFileCoverage($file, $root); | |
if ($srcFile !== null) { | |
$coveralls->addSourceFile($srcFile); | |
} | |
} | |
} | |
return $coveralls; | |
} | |
// Internal method | |
/** | |
* Collect coverage data of a file. | |
* | |
* @param SimpleXMLElement $file Clover XML object of a file. | |
* @param string $root Path to src directory. | |
* @return SourceFile | |
*/ | |
protected function collectFileCoverage(SimpleXMLElement $file, $root) | |
{ | |
$fullpath = (string)$file['name']; | |
if (false === $pos = strpos($fullpath, $root)) { | |
return null; | |
} | |
$filename = str_replace($root, '', $fullpath); | |
return $this->collectCoverage($file, $fullpath, $filename); | |
} | |
/** | |
* Collect coverage data. | |
* | |
* @param SimpleXMLElement $file Clover XML object of a file. | |
* @param string $path Path to source file. | |
* @param string $filename Filename. | |
* @return SourceFile | |
*/ | |
protected function collectCoverage(SimpleXMLElement $file, $path, $filename) | |
{ | |
$srcFile = new SourceFile($path, $filename); | |
foreach ($file->line as $line) { | |
$lineNum = (int)$line['num']; | |
if ($lineNum > 0) { | |
$srcFile->addCoverage($lineNum - 1, (int)$line['count']); | |
} | |
} | |
return $srcFile; | |
} | |
} | |
// rest client | |
class RestClient | |
{ | |
public function upload($url, $path, $filename) | |
{ | |
$post = array( | |
$filename => '@' . $path, | |
); | |
$curl = curl_init(); | |
curl_setopt_array( | |
$curl, | |
array( | |
CURLOPT_URL => $url, | |
CURLOPT_POST => true, | |
CURLOPT_POSTFIELDS => $post, | |
CURLOPT_RETURNTRANSFER => true, | |
) | |
); | |
$data = curl_exec($curl); | |
$info = curl_getinfo($curl); | |
if ($info['http_code'] != 200) { | |
$message = sprintf('Failed to call API. status code: %d', $info['http_code']); | |
throw new \RuntimeException($message); | |
} | |
curl_close($curl); | |
return $data; | |
} | |
} | |
// run | |
$xmlFilename = "clover.xml"; | |
$root = realpath(__DIR__ . "/.."); | |
$logs = realpath("$root/build/logs"); | |
$path = realpath("$logs/$xmlFilename"); | |
$src = "$root/src/"; | |
if ($path === false || !file_exists($path)) { | |
die("Not found $xmlFilename"); | |
} | |
// collect coverage | |
$xml = simplexml_load_file($path); | |
$collector = new CloverXmlCoverageCollector(); | |
$coveralls = $collector->collect($xml, $src); | |
// set token | |
$token = 'your-token-here'; | |
$coveralls->setRepoToken($token); | |
// dump | |
$coverallsJson = "$logs/coveralls.json"; | |
file_put_contents($coverallsJson, $coveralls); | |
// upload file | |
$url = 'https://coveralls.io/api/v1/jobs'; | |
$client = new RestClient(); | |
$data = $client->upload($url, $coverallsJson, 'json_file'); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment