From c7f66e7694fbc055d67e87bfac45c9a3c2a3db41 Mon Sep 17 00:00:00 2001 From: Manuel Schmidt <4742603+trailsnail@users.noreply.github.com> Date: Mon, 26 Jun 2023 09:59:53 +0200 Subject: [PATCH] ORGA-2151 generate trend result file in json to allow later formatting (#121) --- bin/phpstan-baseline-trend.php | 26 ++++++- lib/TrendApplication.php | 124 ++++++++++++++++++++++++--------- lib/TrendResult.php | 34 +++++++++ tests/TrendApplicationTest.php | 60 +++++++++++++++- 4 files changed, 209 insertions(+), 35 deletions(-) create mode 100644 lib/TrendResult.php diff --git a/bin/phpstan-baseline-trend.php b/bin/phpstan-baseline-trend.php index 2e9c403..44968c0 100644 --- a/bin/phpstan-baseline-trend.php +++ b/bin/phpstan-baseline-trend.php @@ -1,6 +1,8 @@ start($argv[1], $argv[2]); +$exitCode = $app->start($argv[1], $argv[2], extractOutputFormat($argv)); exit($exitCode); + +/** + * @param list $args + * @return \staabm\PHPStanBaselineAnalysis\TrendApplication::OUTPUT_FORMAT_* + */ +function extractOutputFormat(array $args): string +{ + foreach($args as $arg) { + if (false === strpos($arg, '--format=')) { + continue; + } + + $format = substr($arg, strlen('--format=')); + if (in_array($format, \staabm\PHPStanBaselineAnalysis\TrendApplication::getAllowedOutputFormats(), true)) { + return $format; + } + + throw new \InvalidArgumentException(sprintf('Invalid output format "%s".', $format)); + } + + return \staabm\PHPStanBaselineAnalysis\TrendApplication::OUTPUT_FORMAT_DEFAULT; +} diff --git a/lib/TrendApplication.php b/lib/TrendApplication.php index 438078b..2cbaaa6 100644 --- a/lib/TrendApplication.php +++ b/lib/TrendApplication.php @@ -2,9 +2,7 @@ namespace staabm\PHPStanBaselineAnalysis; -use \Iterator; -use function Safe\file_get_contents; -use function Safe\json_decode; +use function Safe\json_encode; final class TrendApplication { @@ -21,13 +19,32 @@ final class TrendApplication */ const EXIT_WORSE = 2; + public const OUTPUT_FORMAT_DEFAULT = 'text'; + /** + * @api + */ + public const OUTPUT_FORMAT_JSON = 'json'; + + /** + * @return list + */ + public static function getAllowedOutputFormats(): array + { + return [ + self::OUTPUT_FORMAT_DEFAULT, + self::OUTPUT_FORMAT_JSON, + ]; + } + + /** + * @param self::OUTPUT_FORMAT_* $outputFormat * @return self::EXIT_* * @throws \Safe\Exceptions\JsonException * * @throws \Safe\Exceptions\FilesystemException */ - public function start(string $referenceFilePath, string $comparingFilePath): int + public function start(string $referenceFilePath, string $comparingFilePath, string $outputFormat): int { $exitCode = self::EXIT_IMPROVED; @@ -35,39 +52,86 @@ public function start(string $referenceFilePath, string $comparingFilePath): int $reference = $reader->readFile($referenceFilePath); $comparing = $reader->readFile($comparingFilePath); - foreach ($reference as $baselinePath => $result) { - echo 'Analyzing Trend for ' . $baselinePath . "\n"; + if ($outputFormat === self::OUTPUT_FORMAT_JSON) { + return $this->createOutputJson($reference, $comparing, $exitCode); + } + + return $this->createOutputText($reference, $comparing, $exitCode); + } - if (isset($comparing[$baselinePath])) { - $exitCode = $this->compare(ResultPrinter::KEY_OVERALL_ERRORS, $result->overallErrors, $comparing[$baselinePath]->overallErrors, $exitCode); - echo "\n"; + public function help(): void + { + printf('USAGE: phpstan-baseline-trend [--format=json|text]'); + } - $exitCode = $this->compare(ResultPrinter::KEY_CLASSES_COMPLEXITY, $result->classesComplexity, $comparing[$baselinePath]->classesComplexity, $exitCode); - echo "\n"; - $exitCode = $this->compare(ResultPrinter::KEY_DEPRECATIONS, $result->deprecations, $comparing[$baselinePath]->deprecations, $exitCode); - echo "\n"; + /** + * @param array $reference + * @param array $comparing + * @param self::EXIT_* $exitCode + * + * @return self::EXIT_* + */ + private function createOutputText(array $reference, array $comparing, int $exitCode): int + { + foreach ($reference as $baselinePath => $result) { + list($trendResult, $exitCode) = $this->createTrendResult($baselinePath, $comparing, $result, $exitCode); + + echo $trendResult->headline . "\n"; + foreach($trendResult->results as $key => $stats) { + echo ' '.$key.': '.$stats['reference']." -> ".$stats['comparing']." => ".$stats['trend']."\n"; + } + } - $exitCode = $this->compare(ResultPrinter::KEY_INVALID_PHPDOCS, $result->invalidPhpdocs, $comparing[$baselinePath]->invalidPhpdocs, $exitCode); - echo "\n"; + return $exitCode; + } - $exitCode = $this->compare(ResultPrinter::KEY_UNKNOWN_TYPES, $result->unknownTypes, $comparing[$baselinePath]->unknownTypes, $exitCode); - echo "\n"; + /** + * @param array $reference + * @param array $comparing + * @param self::EXIT_* $exitCode + * + * @return self::EXIT_* + */ + private function createOutputJson(array $reference, array $comparing, int $exitCode): int + { + $trendResults = []; + foreach ($reference as $baselinePath => $result) { - $exitCode = $this->compare(ResultPrinter::KEY_ANONYMOUS_VARIABLES, $result->anonymousVariables, $comparing[$baselinePath]->anonymousVariables, $exitCode); - echo "\n"; + list($trendResult, $exitCode) = $this->createTrendResult($baselinePath, $comparing, $result, $exitCode); - $exitCode = $this->compare(ResultPrinter::KEY_UNUSED_SYMBOLS, $result->unusedSymbols, $comparing[$baselinePath]->unusedSymbols, $exitCode); - echo "\n"; - } + $trendResults[] = $trendResult; } + echo json_encode($trendResults); + + return $exitCode; } - public function help(): void + /** + * @param array $comparing + * @param self::EXIT_* $exitCode + * + * @return array{TrendResult, self::EXIT_*} + */ + private function createTrendResult(string $baselinePath, array $comparing, AnalyzerResult $reference, int $exitCode): array { - printf('USAGE: phpstan-baseline-trend '); + $trendResult = new TrendResult('Analyzing Trend for ' . $baselinePath); + + if (!isset($comparing[$baselinePath])) { + return array($trendResult, $exitCode); + } + + $exitCode = $this->compare($trendResult, ResultPrinter::KEY_OVERALL_ERRORS, $reference->overallErrors, $comparing[$baselinePath]->overallErrors, $exitCode); + $exitCode = $this->compare($trendResult, ResultPrinter::KEY_CLASSES_COMPLEXITY, $reference->classesComplexity, $comparing[$baselinePath]->classesComplexity, $exitCode); + $exitCode = $this->compare($trendResult, ResultPrinter::KEY_DEPRECATIONS, $reference->deprecations, $comparing[$baselinePath]->deprecations, $exitCode); + $exitCode = $this->compare($trendResult, ResultPrinter::KEY_INVALID_PHPDOCS, $reference->invalidPhpdocs, $comparing[$baselinePath]->invalidPhpdocs, $exitCode); + $exitCode = $this->compare($trendResult, ResultPrinter::KEY_UNKNOWN_TYPES, $reference->unknownTypes, $comparing[$baselinePath]->unknownTypes, $exitCode); + $exitCode = $this->compare($trendResult, ResultPrinter::KEY_ANONYMOUS_VARIABLES, $reference->anonymousVariables, $comparing[$baselinePath]->anonymousVariables, $exitCode); + $exitCode = $this->compare($trendResult, ResultPrinter::KEY_UNUSED_SYMBOLS, $reference->unusedSymbols, $comparing[$baselinePath]->unusedSymbols, $exitCode); + + return array($trendResult, $exitCode); } /** @@ -78,21 +142,19 @@ public function help(): void * * @return self::EXIT_* */ - private function compare($key, $referenceValue, $comparingValue, $exitCode): int + private function compare(TrendResult $trendResult, string $key, $referenceValue, $comparingValue, int $exitCode): int { if ($comparingValue > $referenceValue) { - printf(' %s: %d -> %d => worse', $key, $referenceValue, $comparingValue); - + $trendResult->setKey($key, $referenceValue, $comparingValue, 'worse'); $exitCode = max($exitCode, self::EXIT_WORSE); } elseif ($comparingValue < $referenceValue) { - printf(' %s: %d -> %d => improved', $key, $referenceValue, $comparingValue); - + $trendResult->setKey($key, $referenceValue, $comparingValue, 'improved'); $exitCode = max($exitCode, self::EXIT_IMPROVED); } else { - printf(' %s: %d -> %d => good', $key, $referenceValue, $comparingValue); - + $trendResult->setKey($key, $referenceValue, $comparingValue, 'good'); $exitCode = max($exitCode, self::EXIT_STEADY); } + return $exitCode; } } diff --git a/lib/TrendResult.php b/lib/TrendResult.php new file mode 100644 index 0000000..e160288 --- /dev/null +++ b/lib/TrendResult.php @@ -0,0 +1,34 @@ + + */ + public array $results; + + public function __construct(string $headline) + { + $this->headline = $headline; + $this->results = []; + } + + /** + * @param ResultPrinter::KEY_* $key + * @param int $referenceValue + * @param int $comparingValue + * @return void + */ + public function setKey(string $key, $referenceValue, $comparingValue, string $trend): void + { + $this->results[$key] = [ + 'reference' => $referenceValue, + 'comparing' => $comparingValue, + 'trend' => $trend, + ]; + } +} diff --git a/tests/TrendApplicationTest.php b/tests/TrendApplicationTest.php index 10293d3..7d7532c 100644 --- a/tests/TrendApplicationTest.php +++ b/tests/TrendApplicationTest.php @@ -11,7 +11,7 @@ function testSameTrend():void $app = new TrendApplication(); ob_start(); - $exitCode = $app->start(__DIR__.'/fixtures/reference-result.json', __DIR__.'/fixtures/compare-same-result.json'); + $exitCode = $app->start(__DIR__.'/fixtures/reference-result.json', __DIR__.'/fixtures/compare-same-result.json', TrendApplication::OUTPUT_FORMAT_DEFAULT); $rendered = ob_get_clean(); $rendered = str_replace(__DIR__, '', $rendered); @@ -37,7 +37,7 @@ function testHigherTrend():void $app = new TrendApplication(); ob_start(); - $exitCode = $app->start(__DIR__.'/fixtures/reference-result.json', __DIR__.'/fixtures/compare-higher-result.json'); + $exitCode = $app->start(__DIR__.'/fixtures/reference-result.json', __DIR__.'/fixtures/compare-higher-result.json', TrendApplication::OUTPUT_FORMAT_DEFAULT); $rendered = ob_get_clean(); $rendered = str_replace(__DIR__, '', $rendered); @@ -63,7 +63,7 @@ function testLowerTrend():void $app = new TrendApplication(); ob_start(); - $exitCode = $app->start(__DIR__.'/fixtures/reference-result.json', __DIR__.'/fixtures/compare-lower-result.json'); + $exitCode = $app->start(__DIR__.'/fixtures/reference-result.json', __DIR__.'/fixtures/compare-lower-result.json', TrendApplication::OUTPUT_FORMAT_DEFAULT); $rendered = ob_get_clean(); $rendered = str_replace(__DIR__, '', $rendered); @@ -78,6 +78,60 @@ function testLowerTrend():void Anonymous-Variables: 2 -> 1 => improved Unused-Symbols: 1 -> 0 => improved +PHP; + + $this->assertSame($expected, $rendered); + $this->assertSame(TrendApplication::EXIT_IMPROVED, $exitCode); + } + + function testSameTrendFormatJson():void + { + $app = new TrendApplication(); + + ob_start(); + $exitCode = $app->start(__DIR__.'/fixtures/reference-result.json', __DIR__.'/fixtures/compare-same-result.json', TrendApplication::OUTPUT_FORMAT_JSON); + $rendered = ob_get_clean(); + + $rendered = str_replace(__DIR__, '', $rendered); + + $expected = <<assertSame($expected, $rendered); + $this->assertSame(TrendApplication::EXIT_STEADY, $exitCode); + } + + function testHigherTrendFormatJson():void + { + $app = new TrendApplication(); + + ob_start(); + $exitCode = $app->start(__DIR__.'/fixtures/reference-result.json', __DIR__.'/fixtures/compare-higher-result.json', TrendApplication::OUTPUT_FORMAT_JSON); + $rendered = ob_get_clean(); + + $rendered = str_replace(__DIR__, '', $rendered); + + $expected = <<assertSame($expected, $rendered); + $this->assertSame(TrendApplication::EXIT_WORSE, $exitCode); + } + + function testLowerTrendFormatJson():void + { + $app = new TrendApplication(); + + ob_start(); + $exitCode = $app->start(__DIR__.'/fixtures/reference-result.json', __DIR__.'/fixtures/compare-lower-result.json', TrendApplication::OUTPUT_FORMAT_JSON); + $rendered = ob_get_clean(); + + $rendered = str_replace(__DIR__, '', $rendered); + + $expected = <<assertSame($expected, $rendered);