Skip to content

Instantly share code, notes, and snippets.

@toopay
Created July 27, 2013 01:03
Show Gist options
  • Save toopay/6093212 to your computer and use it in GitHub Desktop.
Save toopay/6093212 to your computer and use it in GitHub Desktop.
Coverage Listener to send coveralls payload at its simplest form.
<?php
/*
* This file is part of the Juriya CMF.
*
* (c) Taufan Aditya <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Juriya\Foundation\Tests;
use \SimpleXMLElement;
use Symfony\Component\HttpFoundation\ParameterBag;
/**
* Juriya CMF
*
* @package Juriya\Foundation
* @author Taufan Aditya <[email protected]>
*/
class CoverageListener
{
const COVERAGE_FILE = 'coverage.xml';
const COVERAGE_OUTPUT = 'coverage.json';
/**
* Listener constructor
*
* @param array Argument that sent from phpunit.xml
*/
public function __construct($args = array())
{
// Register the method to collect code-coverage information
register_shutdown_function(function() use ($args) {
CoverageListener::collectAndSendCoverage($args);
});
}
/**
* Main api for collection code-coverage information
*
* @param array Contains repo secret hash and coverage directory
*/
public static function collectAndSendCoverage($args)
{
// Starting point!
self::out("\n\n".'Collecting CodeCoverage...');
if (array_key_exists('repo_token', $args)
&& array_key_exists('target_url', $args)
&& array_key_exists('coverage_dir', $args)
&& array_key_exists('namespace', $args)) {
extract($args);
// Get the realpath coverage directory
$coverage_dir = realpath($coverage_dir);
$coverage_file = $coverage_dir.DIRECTORY_SEPARATOR.self::COVERAGE_FILE;
$coverage_output = $coverage_dir.DIRECTORY_SEPARATOR.self::COVERAGE_OUTPUT;
// Get the coverage information
if (is_dir($coverage_dir) && is_file($coverage_file)) {
// Prepare the coveralls payload
$data = new ParameterBag(array(
'repo_token' => $repo_token,
'source_files' => array(),
));
// Prepare temporary source_files holder
$sourceArray = new ParameterBag();
// Build the coverage xml object
$xml = file_get_contents($coverage_file);
$coverage = new SimpleXMLElement($xml);
if (count($coverage->project->package) > 0) {
// Iterate over the package
foreach ($coverage->project->package as $package) {
// Then itterate on each package file
foreach ($package->file as $packageFile) {
self::printOut('Checking:'.$packageFile['name']);
$sourceArray->add(array(
md5($packageFile['name']) => self::collect($packageFile, $namespace)
));
}
}
}
if (count($coverage->project->file) > 0) {
// itterate over the files
foreach ($coverage->project->file as $file) {
self::printOut('Checking:'.$file['name']);
$sourceArray->add(array(
md5($file['name']) => self::collect($file, $namespace)
));
}
}
// Last, pass the source information it it contains any information
if ($sourceArray->count() > 0) {
$data->set('source_files', array_values($sourceArray->all()));
}
// Write the coverage output
self::out('Writing coverage output...');
file_put_contents($coverage_output, json_encode($data->all(), JSON_NUMERIC_CHECK));
// Send it!
self::out('Sending coverage output...');
$payload = array('json_file'=>'@'.$coverage_output);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url);
curl_setopt($ch, CURLOPT_POST,1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
// Save output into output buffer
ob_get_level() or ob_start();
$result = curl_exec ($ch);
$curlOutput = ob_get_contents();
ob_end_clean();
curl_close ($ch);
self::printOut('cURL Output:'.$curlOutput);
self::printOut('cURL Result:'.$result);
}
}
self::out('Done.');
}
/**
* Collect code-coverage information from a file
*
* @param SimpleXMLElement contains coverage information
* @param string Optional file namespace identifier
* @return array contains code-coverage data with keys as follow : name, source, coverage
*/
public static function collect(SimpleXMLElement $file, $namespace = '')
{
// Get current dir
$currentDir = (isset($_SERVER['PWD'])) ? realpath($_SERVER['PWD']) : getcwd();
// Initial return values
$name = '';
$source = '';
$coverage = array();
// #1 Get the relative file name
list($path, $relativeName) = explode($currentDir, $file['name']);
if (empty($namespace)) {
$name = trim($relativeName, DIRECTORY_SEPARATOR);
} else {
// Replace backslash with directory separator
$ns = str_replace('\\', DIRECTORY_SEPARATOR, $namespace);
list($path, $namespacedName) = explode($ns, $relativeName);
$name = $ns.DIRECTORY_SEPARATOR.trim($namespacedName, DIRECTORY_SEPARATOR);
}
// #2 Build coverage data and the source code
$count = 0;
$handle = fopen($file['name'], "r");
while(!feof($handle)){
$source .= fgets($handle);
$count++;
}
fclose($handle);
// Here we build the default coverage values
$coverage = array_fill(0, $count, null);
// Then, we will overwrite any coverage block into it!
if (count($file->line) > 0) {
foreach ($file->line as $line) {
$attributes = current($line->attributes());
// Only stmt would be count
if (isset($attributes['type'])
&& isset($attributes['count'])
&& $attributes['type'] === 'stmt') {
// Decrease the line number by one
// since key 0 (within coverage array) is actually line number 1
$num = (int) $attributes['num'] - 1;
// Ensure it match count boundaries
if ($num > 0 && $num <= $count) {
$coverage[$num] = (int) $attributes['count'];
}
}
}
}
return compact('name', 'source', 'coverage');
}
/**
* General output CLI response
*
* @param string Output
* @return stream
*/
protected static function out($output = '')
{
fwrite(STDOUT, $output."\n");
}
/**
* Print CLI response with padding
*
* @param string data
* @return stream
*/
protected static function printOut($data)
{
fwrite(STDOUT, str_pad(' ', 2, '*').' '.$data.PHP_EOL);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment