Updating PHPUnit plugin to provide a report like PHPMD and PHPCS, fixes #305

This commit is contained in:
Dan Cryer 2014-05-02 14:36:34 +01:00
parent 4c8083602a
commit 03f3b15fb8
5 changed files with 190 additions and 18 deletions

View file

@ -42,7 +42,7 @@ class Composer implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
$path = $phpci->buildPath;
$this->phpci = $phpci;
$this->directory = isset($options['directory']) ? $path . '/' . $options['directory'] : $path;
$this->action = isset($options['action']) ? $options['action'] : 'update';
$this->action = isset($options['action']) ? $options['action'] : 'install';
$this->preferDist = isset($options['prefer_dist']) ? $options['prefer_dist'] : true;
}

View file

@ -12,6 +12,7 @@ namespace PHPCI\Plugin;
use PHPCI;
use PHPCI\Builder;
use PHPCI\Model\Build;
use PHPCI\Plugin\Util\TapParser;
/**
* PHP Unit Plugin - Allows PHP Unit testing.
@ -23,6 +24,7 @@ class PhpUnit implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
{
protected $args;
protected $phpci;
protected $build;
/**
* @var string|string[] $directory The directory (or array of dirs) to run PHPUnit on
@ -58,20 +60,20 @@ class PhpUnit implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
public static function findConfigFile($buildPath)
{
if (file_exists($buildPath . '/phpunit.xml')) {
return $buildPath . '/phpunit.xml';
if (file_exists($buildPath . 'phpunit.xml')) {
return 'phpunit.xml';
}
if (file_exists($buildPath . '/tests/phpunit.xml')) {
return $buildPath . '/tests/phpunit.xml';
if (file_exists($buildPath . 'tests/phpunit.xml')) {
return 'tests/phpunit.xml';
}
if (file_exists($buildPath . '/phpunit.xml.dist')) {
return $buildPath . '/phpunit.xml.dist';
if (file_exists($buildPath . 'phpunit.xml.dist')) {
return 'phpunit.xml.dist';
}
if (file_exists($buildPath . '/tests/phpunit.xml.dist')) {
return $buildPath . '/tests/phpunit.xml.dist';
if (file_exists($buildPath . 'tests/phpunit.xml.dist')) {
return 'tests/phpunit.xml.dist';
}
return null;
@ -80,9 +82,9 @@ class PhpUnit implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
public function __construct(Builder $phpci, Build $build, array $options = array())
{
$this->phpci = $phpci;
$this->build = $build;
if (!count($options)) {
$this->runFrom = $phpci->buildPath;
if (empty($options['config']) && empty($options['directory'])) {
$this->xmlConfigFile = self::findConfigFile($phpci->buildPath);
}
@ -118,6 +120,8 @@ class PhpUnit implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
{
$success = true;
$this->phpci->logExecOutput(false);
// Run any config files first. This can be either a single value or an array.
if ($this->xmlConfigFile !== null) {
$success &= $this->runConfigFile($this->xmlConfigFile);
@ -128,6 +132,16 @@ class PhpUnit implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
$success &= $this->runDir($this->directory);
}
$output = $this->phpci->getLastOutput();
$tapParser = new TapParser($output);
$output = $tapParser->parse();
$failures = $tapParser->getTotalFailures();
$this->build->storeMeta('phpunit-errors', $failures);
$this->build->storeMeta('phpunit-data', $output);
$this->phpci->logExecOutput(true);
return $success;
}
@ -150,7 +164,7 @@ class PhpUnit implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
}
$cmd = $phpunit . ' %s -c "%s" ' . $this->coverage . $this->path;
$cmd = $phpunit . ' --tap %s -c "%s" ' . $this->coverage . $this->path;
$success = $this->phpci->executeCommand($cmd, $this->args, $this->phpci->buildPath . $configPath);
if ($this->runFrom) {
@ -176,7 +190,7 @@ class PhpUnit implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
return false;
}
$cmd = $phpunit . ' %s "%s"';
$cmd = $phpunit . ' --tap %s "%s"';
$success = $this->phpci->executeCommand($cmd, $this->args, $this->phpci->buildPath . $dirPath);
chdir($curdir);
return $success;

View file

@ -0,0 +1,92 @@
<?php
namespace PHPCI\Plugin\Util;
class TapParser
{
const TEST_COUNTS_PATTERN = '/([0-9]+)\.\.([0-9]+)/';
const TEST_LINE_PATTERN = '/(ok|not ok)\s+[0-9]+\s+\-\s+([^\n]+)::([^\n]+)/';
const TEST_MESSAGE_PATTERN = '/message\:\s+\'([^\']+)\'/';
/**
* @var string
*/
protected $tapString;
protected $failures = 0;
/**
* Create a new TAP parser for a given string.
* @param string $tapString The TAP format string to be parsed.
*/
public function __construct($tapString)
{
$this->tapString = trim($tapString);
}
/**
* Parse a given TAP format string and return an array of tests and their status.
*/
public function parse()
{
// Split up the TAP string into an array of lines, then
// trim all of the lines so there's no leading or trailing whitespace.
$lines = explode("\n", $this->tapString);
$lines = array_map(function ($line) {
return trim($line);
}, $lines);
// Check TAP version:
$versionLine = array_shift($lines);
if ($versionLine != 'TAP version 13') {
throw new \Exception('TapParser only supports TAP version 13');
}
$matches = array();
$totalTests = 0;
if (preg_match(self::TEST_COUNTS_PATTERN, $lines[0], $matches)) {
array_shift($lines);
$totalTests = (int)$matches[2];
}
if (preg_match(self::TEST_COUNTS_PATTERN, $lines[count($lines) - 1], $matches)) {
array_pop($lines);
$totalTests = (int)$matches[2];
}
$rtn = array();
foreach ($lines as $line) {
$matches = array();
if (preg_match(self::TEST_LINE_PATTERN, $line, $matches)) {
$ok = ($matches[1] == 'ok' ? true : false);
if (!$ok) {
$this->failures++;
}
$item = array(
'pass' => $ok,
'suite' => $matches[2],
'test' => $matches[3],
);
$rtn[] = $item;
} elseif (preg_match(self::TEST_MESSAGE_PATTERN, $line, $matches)) {
$rtn[count($rtn) - 1]['message'] = $matches[1];
}
}
if ($totalTests != count($rtn)) {
throw new \Exception('Invalid TAP string, number of tests does not match specified test count.');
}
return $rtn;
}
public function getTotalFailures()
{
return $this->failures;
}
}

View file

@ -0,0 +1,65 @@
var phpunitPlugin = PHPCI.UiPlugin.extend({
id: 'build-phpunit-errors',
css: 'col-lg-12 col-md-12 col-sm-12 col-xs-12',
title: 'PHPUnit',
lastData: null,
displayOnUpdate: false,
box: true,
register: function() {
var self = this;
var query = PHPCI.registerQuery('phpunit-data', -1, {key: 'phpunit-data'})
$(window).on('phpunit-data', function(data) {
self.onUpdate(data);
});
$(window).on('build-updated', function(data) {
if (data.queryData.status > 1) {
self.displayOnUpdate = true;
query();
}
});
},
render: function() {
return $('<table class="table table-striped" id="phpunit-data">' +
'<thead>' +
'<tr>' +
' <th>Test</th>' +
'</tr>' +
'</thead><tbody></tbody></table>');
},
onUpdate: function(e) {
if (this.lastData && this.lastData[0]) {
return;
}
this.lastData = e.queryData;
var tests = this.lastData[0].meta_value;
var tbody = $('#phpunit-data tbody');
tbody.empty();
for (var i in tests) {
var row = $('<tr>' +
'<td><strong>'+tests[i].suite+'' +
'::'+tests[i].test+'</strong><br>' +
''+(tests[i].message || '')+'</td>' +
'</tr>');
if (!tests[i].pass) {
row.addClass('danger');
} else {
row.addClass('success');
}
tbody.append(row);
}
}
});
PHPCI.registerPlugin(new phpunitPlugin());

View file

@ -3,10 +3,11 @@ var warningsPlugin = PHPCI.UiPlugin.extend({
css: 'col-lg-6 col-md-6 col-sm-12 col-xs-12',
title: 'Quality Trend',
keys: {
'phpmd-warnings': 'PHPMD Warnings',
'phpcs-warnings': 'PHPCS Warnings',
'phpcs-errors': 'PHPCS Errors',
'phplint-errors': 'PHPLint Errors'
'phpmd-warnings': 'PHPMD Warnings',
'phpcs-warnings': 'PHPCS Warnings',
'phpcs-errors': 'PHPCS Errors',
'phplint-errors': 'PHPLint Errors',
'phpunit-errors': 'PHPUnit Errors'
},
data: {},
displayOnUpdate: false,
@ -19,7 +20,7 @@ var warningsPlugin = PHPCI.UiPlugin.extend({
queries.push(PHPCI.registerQuery(key, -1, {num_builds: 10, key: key}));
}
$(window).on('phpmd-warnings phpcs-warnings phpcs-errors phplint-errors', function(data) {
$(window).on('phpmd-warnings phpcs-warnings phpcs-errors phplint-errors phpunit-errors', function(data) {
self.onUpdate(data);
});