').append(content).appendTo(tbody);
counts[severity]++;
@@ -141,6 +153,17 @@ var phpunitPlugin = ActiveBuild.UiPlugin.extend({
'';
}
return '???';
+ },
+
+ buildTrace: function(trace){
+ var list = '';
+
+ trace.forEach(function(line){
+ list += '
' + line + '
';
+ });
+ list += '';
+
+ return list;
}
});
diff --git a/src/PHPCensor/Builder.php b/src/PHPCensor/Builder.php
index c46838d5..40280acf 100644
--- a/src/PHPCensor/Builder.php
+++ b/src/PHPCensor/Builder.php
@@ -106,7 +106,7 @@ class Builder implements LoggerAwareInterface
public function __construct(Build $build, LoggerInterface $logger = null)
{
$this->build = $build;
- $this->store = Factory::getStore('Build');
+ $this->store = Factory::getStore('Build', 'PHPCensor');
$this->buildLogger = new BuildLogger($logger, $build);
diff --git a/src/PHPCensor/Languages/lang.en.php b/src/PHPCensor/Languages/lang.en.php
index b63abeb5..5b866c68 100644
--- a/src/PHPCensor/Languages/lang.en.php
+++ b/src/PHPCensor/Languages/lang.en.php
@@ -193,6 +193,7 @@ PHP Censor',
'phpcs_errors' => 'PHPCS Errors',
'phplint_errors' => 'Lint Errors',
'phpunit_errors' => 'PHPUnit Errors',
+ 'phpunit_fail_init' => 'Neither a configuration file nor a test directory found.',
'phpcpd_warnings' => 'PHP Copy/Paste Detector Warnings',
'phpdoccheck_warnings' => 'Missing Docblocks',
'issues' => 'Issues',
diff --git a/src/PHPCensor/Languages/lang.es.php b/src/PHPCensor/Languages/lang.es.php
index df0032aa..b8716f78 100644
--- a/src/PHPCensor/Languages/lang.es.php
+++ b/src/PHPCensor/Languages/lang.es.php
@@ -176,6 +176,7 @@ PHP Censor',
'phpcs_errors' => 'PHPCS Errors',
'phplint_errors' => 'Lint Errors',
'phpunit_errors' => 'PHPUnit Errors',
+ 'phpunit_fail_init' => 'No se encontro archivo o folder de pruevas.',
'phpdoccheck_warnings' => 'Docblocks faltantes',
'issues' => 'Incidencias',
diff --git a/src/PHPCensor/Model/Base/ProjectBase.php b/src/PHPCensor/Model/Base/ProjectBase.php
index 09096d01..e0eff10c 100644
--- a/src/PHPCensor/Model/Base/ProjectBase.php
+++ b/src/PHPCensor/Model/Base/ProjectBase.php
@@ -292,7 +292,7 @@ class ProjectBase extends Model
*/
public function getBuildConfig()
{
- $rtn = $this->data['build_config'];
+ $rtn = $this->data['build_config'];
return $rtn;
}
diff --git a/src/PHPCensor/Model/Build.php b/src/PHPCensor/Model/Build.php
index 2823ef23..58a7eaba 100644
--- a/src/PHPCensor/Model/Build.php
+++ b/src/PHPCensor/Model/Build.php
@@ -96,36 +96,25 @@ class Build extends BuildBase
*/
protected function handleConfig(Builder $builder, $buildPath)
{
- $build_config = null;
+ $build_config = $this->getProject()->getBuildConfig();
- // Try getting the project build config from the database:
if (empty($build_config)) {
- $build_config = $this->getProject()->getBuildConfig();
+ if (file_exists($buildPath . '/.php-censor.yml')) {
+ $build_config = file_get_contents($buildPath . '/.php-censor.yml');
+ } elseif (file_exists($buildPath . '/.phpci.yml')) {
+ $build_config = file_get_contents($buildPath . '/.phpci.yml');
+ } elseif (file_exists($buildPath . '/phpci.yml')) {
+ $build_config = file_get_contents($buildPath . '/phpci.yml');
+ } else {
+ $build_config = $this->getZeroConfigPlugins($builder);
+ }
}
- if (is_file($buildPath . '/.php-censor.yml')) {
- $build_config = file_get_contents($buildPath . '/.php-censor.yml');
- }
-
- if (is_file($buildPath . '/.phpci.yml')) {
- $build_config = file_get_contents($buildPath . '/.phpci.yml');
- }
-
- if (empty($build_config) && is_file($buildPath . '/phpci.yml')) {
- $build_config = file_get_contents($buildPath . '/phpci.yml');
- }
-
- // Fall back to zero config plugins:
- if (empty($build_config)) {
- $build_config = $this->getZeroConfigPlugins($builder);
- }
-
- if (is_string($build_config)) {
- $yamlParser = new YamlParser();
- $build_config = $yamlParser->parse($build_config);
- }
+ $yamlParser = new YamlParser();
+ $build_config = $yamlParser->parse($build_config);
$builder->setConfigArray($build_config);
+
return true;
}
diff --git a/src/PHPCensor/Plugin/Option/PhpUnitOptions.php b/src/PHPCensor/Plugin/Option/PhpUnitOptions.php
new file mode 100644
index 00000000..50a684cd
--- /dev/null
+++ b/src/PHPCensor/Plugin/Option/PhpUnitOptions.php
@@ -0,0 +1,273 @@
+
+ * @package PHPCI
+ * @subpackage Plugin
+ */
+class PhpUnitOptions
+{
+ protected $options;
+ protected $arguments = array();
+
+ public function __construct($options)
+ {
+ $this->options = $options;
+ }
+
+ /**
+ * Remove a command argument
+ *
+ * @param $argumentName
+ *
+ * @return $this
+ */
+ public function removeArgument($argumentName)
+ {
+ unset($this->arguments[$argumentName]);
+ return $this;
+ }
+
+ /**
+ * Combine all the argument into a string for the phpunit command
+ *
+ * @return string
+ */
+ public function buildArgumentString()
+ {
+ $argumentString = '';
+ foreach ($this->getCommandArguments() as $argumentName => $argumentValues) {
+ $prefix = $argumentName[0] == '-' ? '' : '--';
+
+ if (!is_array($argumentValues)) {
+ $argumentValues = array($argumentValues);
+ }
+
+ foreach ($argumentValues as $argValue) {
+ $postfix = ' ';
+ if (!empty($argValue)) {
+ $postfix = ' "' . $argValue . '" ';
+ }
+ $argumentString .= $prefix . $argumentName . $postfix;
+ }
+ }
+
+ return $argumentString;
+ }
+
+ /**
+ * Get all the command arguments
+ *
+ * @return string[]
+ */
+ public function getCommandArguments()
+ {
+ /*
+ * Return the full list of arguments
+ */
+ return $this->parseArguments()->arguments;
+ }
+
+ /**
+ * Parse the arguments from the config options
+ *
+ * @return $this
+ */
+ private function parseArguments()
+ {
+ if (empty($this->arguments)) {
+ /*
+ * Parse the arguments from the YML options file
+ */
+ if (isset($this->options['args'])) {
+ $rawArgs = $this->options['args'];
+ if (is_array($rawArgs)) {
+ $this->arguments = $rawArgs;
+ } else {
+ /*
+ * Try to parse old arguments in a single string
+ */
+ preg_match_all('@--([a-z\-]+)([\s=]+)?[\'"]?((?!--)[-\w/.,\\\]+)?[\'"]?@', (string)$rawArgs, $argsMatch);
+
+ if (!empty($argsMatch) && sizeof($argsMatch) > 2) {
+ foreach ($argsMatch[1] as $index => $argName) {
+ $this->addArgument($argName, $argsMatch[3][$index]);
+ }
+ }
+ }
+ }
+
+ /*
+ * Handles command aliases outside of the args option
+ */
+ if (isset($this->options['coverage'])) {
+ $this->addArgument('coverage-html', $this->options['coverage']);
+ }
+
+ /*
+ * Handles command aliases outside of the args option
+ */
+ if (isset($this->options['config'])) {
+ $this->addArgument('configuration', $this->options['config']);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Add an argument to the collection
+ * Note: adding argument before parsing the options will prevent the other options from been parsed.
+ *
+ * @param string $argumentName
+ * @param string $argumentValue
+ */
+ public function addArgument($argumentName, $argumentValue)
+ {
+ if (isset($this->arguments[$argumentName])) {
+ if (!is_array($this->arguments[$argumentName])) {
+ // Convert existing argument values into an array
+ $this->arguments[$argumentName] = array($this->arguments[$argumentName]);
+ }
+
+ // Appends the new argument to the list
+ $this->arguments[$argumentName][] = $argumentValue;
+ } else {
+ // Adds new argument
+ $this->arguments[$argumentName] = $argumentValue;
+ }
+ }
+
+ /**
+ * Get the list of directory to run phpunit in
+ *
+ * @return string[] List of directories
+ */
+ public function getDirectories()
+ {
+ $directories = $this->getOption('directory');
+
+ if (is_string($directories)) {
+ $directories = array($directories);
+ } else {
+ if (is_null($directories)) {
+ $directories = array();
+ }
+ }
+
+ return is_array($directories) ? $directories : array($directories);
+ }
+
+ /**
+ * Get an option if defined
+ *
+ * @param $optionName
+ *
+ * @return string[]|string|null
+ */
+ public function getOption($optionName)
+ {
+ if (isset($this->options[$optionName])) {
+ return $this->options[$optionName];
+ }
+
+ return null;
+ }
+
+ /**
+ * Get the directory to execute the command from
+ *
+ * @return mixed|null
+ */
+ public function getRunFrom()
+ {
+ return $this->getOption('run_from');
+ }
+
+ /**
+ * Ge the directory name where tests file reside
+ *
+ * @return string|null
+ */
+ public function getTestsPath()
+ {
+ return $this->getOption('path');
+ }
+
+ /**
+ * Get the PHPUnit configuration from the options, or the optional path
+ *
+ * @param string $altPath
+ *
+ * @return string[] path of files
+ */
+ public function getConfigFiles($altPath = null)
+ {
+ $configFiles = $this->getArgument('configuration');
+ if (empty($configFiles) && $altPath) {
+ $configFile = self::findConfigFile($altPath);
+ if ($configFile) {
+ $configFiles[] = $configFile;
+ }
+ }
+
+ return $configFiles;
+ }
+
+ /**
+ * Get options for a given argument
+ *
+ * @param $argumentName
+ *
+ * @return string[] All the options for given argument
+ */
+ public function getArgument($argumentName)
+ {
+ $this->parseArguments();
+
+ if (isset($this->arguments[$argumentName])) {
+ return is_array(
+ $this->arguments[$argumentName]
+ ) ? $this->arguments[$argumentName] : array($this->arguments[$argumentName]);
+ }
+
+ return array();
+ }
+
+ /**
+ * Find a PHPUnit configuration file in a directory
+ *
+ * @param string $buildPath The path to configuration file
+ *
+ * @return null|string
+ */
+ public static function findConfigFile($buildPath)
+ {
+ $files = array(
+ 'phpunit.xml',
+ 'phpunit.xml.dist',
+ 'tests/phpunit.xml',
+ 'tests/phpunit.xml.dist',
+ );
+
+ foreach ($files as $file) {
+ if (file_exists($buildPath . $file)) {
+ return $file;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/PHPCensor/Plugin/PhpUnit.php b/src/PHPCensor/Plugin/PhpUnit.php
index 444ffb9b..666ae0ff 100644
--- a/src/PHPCensor/Plugin/PhpUnit.php
+++ b/src/PHPCensor/Plugin/PhpUnit.php
@@ -1,4 +1,5 @@
-* @package PHPCI
-* @subpackage Plugins
-*/
+ * PHP Unit Plugin - A rewrite of the original PHP Unit plugin
+ *
+ * @author Dan Cryer
+ * @author Pablo Tejada
+ * @package PHPCI
+ * @subpackage Plugins
+ */
class PhpUnit implements PHPCensor\Plugin, PHPCensor\ZeroConfigPlugin
{
- protected $args;
protected $phpci;
protected $build;
+ /** @var string[] Raw options from the PHPCI config file */
+ protected $options = array();
+
/**
- * @var string|string[] $directory The directory (or array of dirs) to run PHPUnit on
+ * Standard Constructor
+ * $options['config'] Path to a PHPUnit XML configuration file.
+ * $options['run_from'] The directory where the phpunit command will run from when using 'config'.
+ * $options['coverage'] Value for the --coverage-html command line flag.
+ * $options['directory'] Optional directory or list of directories to run PHPUnit on.
+ * $options['args'] Command line args (in string format) to pass to PHP Unit
+ *
+ * @param Builder $phpci
+ * @param Build $build
+ * @param string[] $options
*/
- protected $directory;
+ public function __construct(Builder $phpci, Build $build, array $options = array())
+ {
+ $this->phpci = $phpci;
+ $this->build = $build;
+ $this->options = new PhpUnitOptions($options);
+ }
/**
- * @var string $runFrom When running PHPUnit with an XML config, the command is run from this directory
- */
- protected $runFrom;
-
- /**
- * @var string, in cases where tests files are in a sub path of the /tests path,
- * allows this path to be set in the config.
- */
- protected $path;
-
- protected $coverage = "";
-
- /**
- * @var string|string[] $xmlConfigFile The path (or array of paths) of an xml config for PHPUnit
- */
- protected $xmlConfigFile;
-
- /**
- * Check if this plugin can be executed.
- * @param $stage
+ * Check if the plugin can be executed without any configurations
+ *
+ * @param $stage
* @param Builder $builder
- * @param Build $build
+ * @param Build $build
+ *
* @return bool
*/
public static function canExecute($stage, Builder $builder, Build $build)
{
- if ($stage == 'test' && !is_null(self::findConfigFile($builder->buildPath))) {
+ if ($stage == 'test' && !is_null(PhpUnitOptions::findConfigFile($build->getBuildPath()))) {
return true;
}
@@ -66,185 +72,118 @@ class PhpUnit implements PHPCensor\Plugin, PHPCensor\ZeroConfigPlugin
}
/**
- * Try and find the phpunit XML config file.
- * @param $buildPath
- * @return null|string
+ * Runs PHP Unit tests in a specified directory, optionally using specified config file(s).
*/
- public static function findConfigFile($buildPath)
- {
- if (file_exists($buildPath . 'phpunit.xml')) {
- return 'phpunit.xml';
- }
-
- if (file_exists($buildPath . 'tests' . DIRECTORY_SEPARATOR . 'phpunit.xml')) {
- return 'tests' . DIRECTORY_SEPARATOR . 'phpunit.xml';
- }
-
- if (file_exists($buildPath . 'phpunit.xml.dist')) {
- return 'phpunit.xml.dist';
- }
-
- if (file_exists($buildPath . 'tests/phpunit.xml.dist')) {
- return 'tests' . DIRECTORY_SEPARATOR . 'phpunit.xml.dist';
- }
-
- return null;
- }
-
- /**
- * Standard Constructor
- *
- * $options['directory'] Output Directory. Default: %BUILDPATH%
- * $options['filename'] Phar Filename. Default: build.phar
- * $options['regexp'] Regular Expression Filename Capture. Default: /\.php$/
- * $options['stub'] Stub Content. No Default Value
- *
- * @param Builder $phpci
- * @param Build $build
- * @param array $options
- */
- public function __construct(Builder $phpci, Build $build, array $options = [])
- {
- $this->phpci = $phpci;
- $this->build = $build;
-
- if (empty($options['config']) && empty($options['directory'])) {
- $this->xmlConfigFile = self::findConfigFile($phpci->buildPath);
- }
-
- if (isset($options['directory'])) {
- $this->directory = $options['directory'];
- }
-
- if (isset($options['config'])) {
- $this->xmlConfigFile = $options['config'];
- }
-
- if (isset($options['run_from'])) {
- $this->runFrom = $options['run_from'];
- }
-
- if (isset($options['args'])) {
- $this->args = $this->phpci->interpolate($options['args']);
- }
-
- if (isset($options['path'])) {
- $this->path = $options['path'];
- }
-
- if (isset($options['coverage'])) {
- $this->coverage = ' --coverage-html ' . $this->phpci->interpolate($options['coverage']) . ' ';
- }
-
- $this->phpci->logDebug('Plugin options: ' . json_encode($options));
- }
-
- /**
- * Runs PHP Unit tests in a specified directory, optionally using specified config file(s).
- */
public function execute()
{
- if (empty($this->xmlConfigFile) && empty($this->directory)) {
- $this->phpci->logFailure('Neither configuration file nor test directory found.');
+ $xmlConfigFiles = $this->options->getConfigFiles($this->build->getBuildPath());
+ $directories = $this->options->getDirectories();
+ if (empty($xmlConfigFiles) && empty($directories)) {
+ $this->phpci->logFailure(Lang::get('phpunit_fail_init'));
return false;
}
- $success = true;
+ $success = array();
- $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);
+ // Run any directories
+ if (!empty($directories)) {
+ foreach ($directories as $directory) {
+ $success[] = $this->runDir($directory);
+ }
+ } else {
+ // Run any config files
+ if (!empty($xmlConfigFiles)) {
+ foreach ($xmlConfigFiles as $configFile) {
+ $success[] = $this->runConfigFile($configFile);
+ }
+ }
}
- // Run any dirs next. Again this can be either a single value or an array.
- if ($this->directory !== null) {
- $success &= $this->runDir($this->directory);
- }
+ return !in_array(false, $success);
+ }
- $tapString = $this->phpci->getLastOutput();
- $tapString = mb_convert_encoding($tapString, "UTF-8", "ISO-8859-1");
+ /**
+ * Run the PHPUnit tests in a specific directory or array of directories.
+ *
+ * @param $directory
+ *
+ * @return bool|mixed
+ */
+ protected function runDir($directory)
+ {
+ $options = clone $this->options;
- try {
- $tapParser = new TapParser($tapString);
- $output = $tapParser->parse();
- } catch (\Exception $ex) {
- $this->phpci->logFailure($tapString);
- throw $ex;
- }
+ // Save the results into a json file
+ $jsonFile = tempnam(RUNTIME_DIR, 'jLog_');
+ $options->addArgument('log-json', $jsonFile);
- $failures = $tapParser->getTotalFailures();
+ // Removes any current configurations files
+ $options->removeArgument('configuration');
- $this->build->storeMeta('phpunit-errors', $failures);
- $this->build->storeMeta('phpunit-data', $output);
+ $arguments = $this->phpci->interpolate($options->buildArgumentString());
+ $cmd = $this->phpci->findBinary('phpunit') . ' %s "%s"';
+ $success = $this->phpci->executeCommand($cmd, $arguments, $directory);
- $this->phpci->logExecOutput(true);
+ $this->processResults($jsonFile);
return $success;
}
/**
* Run the tests defined in a PHPUnit config file.
- * @param $configPath
+ *
+ * @param $configFile
+ *
* @return bool|mixed
*/
- protected function runConfigFile($configPath)
+ protected function runConfigFile($configFile)
{
- if (is_array($configPath)) {
- return $this->recurseArg($configPath, [$this, "runConfigFile"]);
- } else {
- if ($this->runFrom) {
- $curdir = getcwd();
- chdir($this->phpci->buildPath . DIRECTORY_SEPARATOR . $this->runFrom);
- }
+ $options = clone $this->options;
+ $buildPath = $this->build->getBuildPath() . DIRECTORY_SEPARATOR;
- $phpunit = $this->phpci->findBinary('phpunit');
+ // Save the results into a json file
+ $jsonFile = tempnam(RUNTIME_DIR, 'jLog_');
+ $options->addArgument('log-json', $jsonFile);
- $cmd = $phpunit . ' --tap %s -c "%s" ' . $this->coverage . $this->path;
- $success = $this->phpci->executeCommand($cmd, $this->args, $this->phpci->buildPath . $configPath);
+ // Removes any current configurations files
+ $options->removeArgument('configuration');
+ // Only the add the configuration file been passed
+ $options->addArgument('configuration', $buildPath . $configFile);
- if ($this->runFrom) {
- chdir($curdir);
- }
+ $arguments = $this->phpci->interpolate($options->buildArgumentString());
+ $cmd = $this->phpci->findBinary('phpunit') . ' %s %s';
+ $success = $this->phpci->executeCommand($cmd, $arguments, $options->getTestsPath());
- return $success;
- }
- }
+ $this->processResults($jsonFile);
- /**
- * Run the PHPUnit tests in a specific directory or array of directories.
- * @param $directory
- * @return bool|mixed
- */
- protected function runDir($directory)
- {
- if (is_array($directory)) {
- return $this->recurseArg($directory, [$this, "runDir"]);
- } else {
- $curdir = getcwd();
- chdir($this->phpci->buildPath);
-
- $phpunit = $this->phpci->findBinary('phpunit');
-
- $cmd = $phpunit . ' --tap %s "%s"';
- $success = $this->phpci->executeCommand($cmd, $this->args, $this->phpci->buildPath . $directory);
- chdir($curdir);
- return $success;
- }
- }
-
- /**
- * @param $array
- * @param $callable
- * @return bool|mixed
- */
- protected function recurseArg($array, $callable)
- {
- $success = true;
- foreach ($array as $subItem) {
- $success &= call_user_func($callable, $subItem);
- }
return $success;
}
+
+ /**
+ * Saves the test results
+ *
+ * @param string $jsonFile
+ *
+ * @throws \Exception If the failed to parse the JSON file
+ */
+ protected function processResults($jsonFile)
+ {
+ var_dump('fuck!');
+ if (file_exists($jsonFile)) {
+ $parser = new PhpUnitResult($jsonFile, $this->build->getBuildPath());
+
+ $this->build->storeMeta('phpunit-data', $parser->parse()->getResults());
+ $this->build->storeMeta('phpunit-errors', $parser->getFailures());
+
+ foreach ($parser->getErrors() as $error) {
+ $severity = $error['severity'] == $parser::SEVERITY_ERROR ? BuildError::SEVERITY_CRITICAL : BuildError::SEVERITY_HIGH;
+ $this->build->reportError(
+ $this->phpci, 'php_unit', $error['message'], $severity, $error['file'], $error['line']
+ );
+ }
+ @unlink($jsonFile);
+ } else {
+ throw new \Exception('JSON output file does not exist: ' . $jsonFile);
+ }
+ }
}
diff --git a/src/PHPCensor/Plugin/Util/PhpUnitResult.php b/src/PHPCensor/Plugin/Util/PhpUnitResult.php
new file mode 100644
index 00000000..a8264e21
--- /dev/null
+++ b/src/PHPCensor/Plugin/Util/PhpUnitResult.php
@@ -0,0 +1,235 @@
+
+ * @package PHPCI
+ * @subpackage Plugin
+ */
+class PhpUnitResult
+{
+ const EVENT_TEST = 'test';
+ const EVENT_TEST_START = 'testStart';
+ const EVENT_SUITE_START = 'suiteStart';
+
+ const SEVERITY_PASS = 'success';
+ const SEVERITY_FAIL = 'fail';
+ const SEVERITY_ERROR = 'error';
+ const SEVERITY_SKIPPED = 'skipped';
+
+ protected $options;
+ protected $arguments = array();
+ protected $results;
+ protected $failures = 0;
+ protected $errors = array();
+
+ public function __construct($outputFile, $buildPath = '')
+ {
+ $this->outputFile = $outputFile;
+ $this->buildPath = $buildPath;
+ }
+
+ /**
+ * Parse the results
+ *
+ * @return $this
+ * @throws \Exception If fails to parse the output
+ */
+ public function parse()
+ {
+ $rawResults = file_get_contents($this->outputFile);
+ if (empty($rawResults)) {
+ throw new \Exception('No test executed.');
+ }
+ if ($rawResults[0] == '{') {
+ $fixedJson = '[' . str_replace('}{', '},{', $rawResults) . ']';
+ $events = json_decode($fixedJson, true);
+ } else {
+ $events = json_decode($rawResults, true);
+ }
+
+ // Reset the parsing variables
+ $this->results = array();
+ $this->errors = array();
+ $this->failures = 0;
+
+ if (is_array($events)) {
+ foreach ($events as $event) {
+ if ($event['event'] == self::EVENT_TEST) {
+ $this->results[] = $this->parseEvent($event);
+ }
+ }
+ } else {
+ throw new \Exception('Failed to parse the JSON output.');
+ }
+
+ return $this;
+ }
+
+ /**
+ * Parse a test event
+ *
+ * @param array $event
+ *
+ * @return string[]
+ */
+ protected function parseEvent($event)
+ {
+ list($pass, $severity) = $this->getStatus($event);
+
+ $data = array(
+ 'pass' => $pass,
+ 'severity' => $severity,
+ 'message' => $this->buildMessage($event),
+ 'trace' => $pass ? array() : $this->buildTrace($event),
+ 'output' => $event['output'],
+ );
+
+ if (!$pass) {
+ $this->failures++;
+ $this->addError($data, $event);
+ }
+
+ return $data;
+ }
+
+ /**
+ * Build the status of the event
+ *
+ * @param $event
+ *
+ * @return mixed[bool,string] - The pass and severity flags
+ * @throws \Exception
+ */
+ protected function getStatus($event)
+ {
+ $status = $event['status'];
+ switch ($status) {
+ case 'fail':
+ $pass = false;
+ $severity = self::SEVERITY_FAIL;
+ break;
+ case 'error':
+ if (strpos($event['message'], 'Skipped') === 0 || strpos($event['message'], 'Incomplete') === 0) {
+ $pass = true;
+ $severity = self::SEVERITY_SKIPPED;
+ } else {
+ $pass = false;
+ $severity = self::SEVERITY_ERROR;
+ }
+ break;
+ case 'pass':
+ $pass = true;
+ $severity = self::SEVERITY_PASS;
+ break;
+ case 'warning':
+ $pass = true;
+ $severity = self::SEVERITY_PASS;
+ break;
+ default:
+ throw new \Exception("Unexpected PHPUnit test status: {$status}");
+ break;
+ }
+
+ return array($pass, $severity);
+ }
+
+ /**
+ * Build the message string for an event
+ *
+ * @param array $event
+ *
+ * @return string
+ */
+ protected function buildMessage($event)
+ {
+ $message = $event['test'];
+
+ if ($event['message']) {
+ $message .= PHP_EOL . $event ['message'];
+ }
+
+ return $message;
+ }
+
+ /**
+ * Build a string base trace of the failure
+ *
+ * @param array $event
+ *
+ * @return string[]
+ */
+ protected function buildTrace($event)
+ {
+ $formattedTrace = array();
+
+ if (!empty($event['trace'])) {
+ foreach ($event['trace'] as $step){
+ $line = str_replace($this->buildPath, '', $step['file']) . ':' . $step['line'];
+ $formattedTrace[] = $line;
+ }
+ }
+
+ return $formattedTrace;
+ }
+
+ /**
+ * Saves additional info for a failing test
+ *
+ * @param array $data
+ * @param array $event
+ */
+ protected function addError($data, $event)
+ {
+ $firstTrace = end($event['trace']);
+ reset($event['trace']);
+
+ $this->errors[] = array(
+ 'message' => $data['message'],
+ 'severity' => $data['severity'],
+ 'file' => str_replace($this->buildPath, '', $firstTrace['file']),
+ 'line' => $firstTrace['line'],
+ );
+ }
+
+ /**
+ * Get the parse results
+ *
+ * @return string[]
+ */
+ public function getResults()
+ {
+ return $this->results;
+ }
+
+ /**
+ * Get the total number of failing tests
+ *
+ * @return int
+ */
+ public function getFailures()
+ {
+ return $this->failures;
+ }
+
+ /**
+ * Get the tests with failing status
+ *
+ * @return string[]
+ */
+ public function getErrors()
+ {
+ return $this->errors;
+ }
+}
diff --git a/src/PHPCensor/View/Build/errors.phtml b/src/PHPCensor/View/Build/errors.phtml
index 55d1ae7b..8a383c92 100644
--- a/src/PHPCensor/View/Build/errors.phtml
+++ b/src/PHPCensor/View/Build/errors.phtml
@@ -3,7 +3,7 @@ use PHPCensor\Helper\Lang;
$linkTemplate = $build->getFileLinkTemplate();
-/** @var \PHPCensor\Model\BuildError $error */
+/** @var \PHPCI\Model\BuildError[] $errors */
foreach ($errors as $error):
$link = str_replace('{FILE}', $error->getFile(), $linkTemplate);
@@ -30,8 +30,8 @@ foreach ($errors as $error):
?>
-
getMessage(); ?>
+
getMessage(); ?>
-
\ No newline at end of file
+
diff --git a/src/PHPCensor/View/layout.phtml b/src/PHPCensor/View/layout.phtml
index 043af1f4..40b6cba6 100644
--- a/src/PHPCensor/View/layout.phtml
+++ b/src/PHPCensor/View/layout.phtml
@@ -17,6 +17,11 @@
+