From c3c103cc78362bda1a3d407e2a55b575e7556d36 Mon Sep 17 00:00:00 2001 From: SimonHeimberg Date: Sat, 15 Jul 2017 02:18:07 +0200 Subject: [PATCH] prepare for several types of phpunit results --- src/PHPCensor/Plugin/PhpUnit.php | 14 +- src/PHPCensor/Plugin/Util/PhpUnitResult.php | 176 +++--------------- .../Plugin/Util/PhpUnitResultJson.php | 145 +++++++++++++++ .../Plugin/Util/PhpUnitResultTest.php | 3 +- 4 files changed, 185 insertions(+), 153 deletions(-) create mode 100644 src/PHPCensor/Plugin/Util/PhpUnitResultJson.php diff --git a/src/PHPCensor/Plugin/PhpUnit.php b/src/PHPCensor/Plugin/PhpUnit.php index a1100e3b..0e143e04 100644 --- a/src/PHPCensor/Plugin/PhpUnit.php +++ b/src/PHPCensor/Plugin/PhpUnit.php @@ -7,7 +7,7 @@ use PHPCensor\Builder; use PHPCensor\Model\Build; use PHPCensor\Model\BuildError; use PHPCensor\Plugin\Option\PhpUnitOptions; -use PHPCensor\Plugin\Util\PhpUnitResult; +use PHPCensor\Plugin\Util\PhpUnitResultJson; use PHPCensor\Plugin; use PHPCensor\ZeroConfigPluginInterface; @@ -112,8 +112,8 @@ class PhpUnit extends Plugin implements ZeroConfigPluginInterface $buildPath = $this->build->getBuildPath() . DIRECTORY_SEPARATOR; // Save the results into a json file - $jsonFile = @tempnam($buildPath, 'jLog_'); - $options->addArgument('log-json', $jsonFile); + $logFile = @tempnam($buildPath, 'jLog_'); + $options->addArgument('log-json', $logFile); // Removes any current configurations files $options->removeArgument('configuration'); @@ -126,7 +126,7 @@ class PhpUnit extends Plugin implements ZeroConfigPluginInterface $cmd = $this->findBinary('phpunit') . ' %s %s'; $success = $this->builder->executeCommand($cmd, $arguments, $directory); - $this->processResults($jsonFile); + $this->processResults($logFile); return $success; } @@ -141,7 +141,7 @@ class PhpUnit extends Plugin implements ZeroConfigPluginInterface protected function processResults($jsonFile) { if (file_exists($jsonFile)) { - $parser = new PhpUnitResult($jsonFile, $this->build->getBuildPath()); + $parser = new PhpUnitResultJson($logFile, $this->build->getBuildPath()); $this->build->storeMeta('phpunit-data', $parser->parse()->getResults()); $this->build->storeMeta('phpunit-errors', $parser->getFailures()); @@ -152,9 +152,9 @@ class PhpUnit extends Plugin implements ZeroConfigPluginInterface $this->builder, 'php_unit', $error['message'], $severity, $error['file'], $error['line'] ); } - @unlink($jsonFile); + @unlink($logFile); } else { - throw new \Exception('JSON output file does not exist: ' . $jsonFile); + throw new \Exception('log output file does not exist: ' . $logFile); } } } diff --git a/src/PHPCensor/Plugin/Util/PhpUnitResult.php b/src/PHPCensor/Plugin/Util/PhpUnitResult.php index 000d2e70..10ef2fa1 100644 --- a/src/PHPCensor/Plugin/Util/PhpUnitResult.php +++ b/src/PHPCensor/Plugin/Util/PhpUnitResult.php @@ -7,19 +7,15 @@ namespace PHPCensor\Plugin\Util; * * @author Pablo Tejada */ -class PhpUnitResult +abstract 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 = []; + protected $outputFile; + protected $buildPath; protected $results; protected $failures = 0; protected $errors = []; @@ -36,158 +32,48 @@ class PhpUnitResult * @return $this * @throws \Exception If fails to parse the output */ - public function parse() + abstract public function parse(); + + abstract protected function getSeverity($testcase); + + abstract protected function buildMessage($testcase); + + abstract protected function buildTrace($testcase); + + protected function getFileAndLine($testcase) { - $rawResults = file_get_contents($this->outputFile); - - $events = []; - if ($rawResults && $rawResults[0] == '{') { - $fixedJson = '[' . str_replace('}{', '},{', $rawResults) . ']'; - $events = json_decode($fixedJson, true); - } elseif ($rawResults) { - $events = json_decode($rawResults, true); - } - - // Reset the parsing variables - $this->results = []; - $this->errors = []; - $this->failures = 0; - - if ($events) { - foreach ($events as $event) { - if (isset($event['event']) && $event['event'] == self::EVENT_TEST) { - $this->results[] = $this->parseEvent($event); - } - } - } - - return $this; + return $testcase; } - /** - * Parse a test event - * - * @param array $event - * - * @return string[] - */ - protected function parseEvent($event) + protected function getOutput($testcase) { - list($pass, $severity) = $this->getStatus($event); + return $testcase['output']; + } + protected function parseTestcase($testcase) + { + $severity = $this->getSeverity($testcase); + $pass = isset(array_fill_keys([self::SEVERITY_PASS, self::SEVERITY_SKIPPED], true)[$severity]); $data = [ 'pass' => $pass, 'severity' => $severity, - 'message' => $this->buildMessage($event), - 'trace' => $pass ? [] : $this->buildTrace($event), - 'output' => $event['output'], + 'message' => $this->buildMessage($testcase), + 'trace' => $pass ? [] : $this->buildTrace($testcase), + 'output' => $this->getOutput($testcase), ]; if (!$pass) { $this->failures++; - $this->addError($data, $event); + $info = $this->getFileAndLine($testcase); + $this->errors[] = [ + 'message' => $data['message'], + 'severity' => $severity, + 'file' => $info['file'], + 'line' => $info['line'], + ]; } - 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 [$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 = []; - - 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[] = [ - 'message' => $data['message'], - 'severity' => $data['severity'], - 'file' => str_replace($this->buildPath, '', $firstTrace['file']), - 'line' => $firstTrace['line'], - ]; + $this->results[] = $data; } /** diff --git a/src/PHPCensor/Plugin/Util/PhpUnitResultJson.php b/src/PHPCensor/Plugin/Util/PhpUnitResultJson.php new file mode 100644 index 00000000..c99704cb --- /dev/null +++ b/src/PHPCensor/Plugin/Util/PhpUnitResultJson.php @@ -0,0 +1,145 @@ + + */ +class PhpUnitResultJson extends PhpUnitResult +{ + const EVENT_TEST = 'test'; + const EVENT_TEST_START = 'testStart'; + const EVENT_SUITE_START = 'suiteStart'; + + protected $options; + protected $arguments = []; + + /** + * Parse the results + * + * @return $this + * @throws \Exception If fails to parse the output + */ + public function parse() + { + $rawResults = file_get_contents($this->outputFile); + + $events = []; + if ($rawResults && $rawResults[0] == '{') { + $fixedJson = '[' . str_replace('}{', '},{', $rawResults) . ']'; + $events = json_decode($fixedJson, true); + } elseif ($rawResults) { + $events = json_decode($rawResults, true); + } + + // Reset the parsing variables + $this->results = []; + $this->errors = []; + $this->failures = 0; + + if ($events) { + foreach ($events as $event) { + if (isset($event['event']) && $event['event'] == self::EVENT_TEST) { + $this->parseTestcase($event); + } + } + } + + return $this; + } + + + /** + * Build the severity of the event + * + * @param $event + * + * @return string The severity flags + * @throws \Exception + */ + protected function getSeverity($event) + { + $status = $event['status']; + switch ($status) { + case 'fail': + $severity = self::SEVERITY_FAIL; + break; + case 'error': + if (strpos($event['message'], 'Skipped') === 0 || strpos($event['message'], 'Incomplete') === 0) { + $severity = self::SEVERITY_SKIPPED; + } else { + $severity = self::SEVERITY_ERROR; + } + break; + case 'pass': + $severity = self::SEVERITY_PASS; + break; + case 'warning': + $severity = self::SEVERITY_PASS; + break; + default: + throw new \Exception("Unexpected PHPUnit test status: {$status}"); + break; + } + + return $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 = []; + + 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 getFileAndLine($event) + { + $firstTrace = end($event['trace']); + reset($event['trace']); + + return [ + 'file' => str_replace($this->buildPath, '', $firstTrace['file']), + 'line' => $firstTrace['line'] + ]; + } +} diff --git a/tests/PHPCensor/Plugin/Util/PhpUnitResultTest.php b/tests/PHPCensor/Plugin/Util/PhpUnitResultTest.php index 95eab8dd..55ea7f50 100644 --- a/tests/PHPCensor/Plugin/Util/PhpUnitResultTest.php +++ b/tests/PHPCensor/Plugin/Util/PhpUnitResultTest.php @@ -3,6 +3,7 @@ namespace Tests\PHPCensor\Plugin\Util; use PHPCensor\Plugin\Util\PhpUnitResult; +use PHPCensor\Plugin\Util\PhpUnitResultJson; /** * Class PhpUnitResultTest parses the results for the PhpUnitV2 plugin @@ -16,7 +17,7 @@ class PhpUnitResultTest extends \PHPUnit_Framework_TestCase public function testInitParse() { $buildPath = '/path/to/build'; - $parser = new PhpUnitResult(ROOT_DIR . 'tests/PHPCensor/Plugin/SampleFiles/phpunit_money.txt', $buildPath); + $parser = new PhpUnitResultJson(ROOT_DIR . 'tests/PHPCensor/Plugin/SampleFiles/phpunit_money.txt', $buildPath); $output = $parser->parse()->getResults(); $errors = $parser->getErrors();