From 6a84b825e646c5df75e5992719db3f6106d12976 Mon Sep 17 00:00:00 2001 From: luke Date: Fri, 30 Jul 2021 12:23:24 +0100 Subject: [PATCH] Output improvements. --- composer.json | 2 +- src/Logging/TeamCity.php | 391 +++++++++++++++++------------------ tests/.snapshots/success.txt | 8 +- 3 files changed, 192 insertions(+), 209 deletions(-) diff --git a/composer.json b/composer.json index bee8f8b7..d3feddb8 100644 --- a/composer.json +++ b/composer.json @@ -57,7 +57,7 @@ "scripts": { "lint": "php-cs-fixer fix -v", "test:lint": "php-cs-fixer fix -v --dry-run", - "test:types": "phpstan analyse --ansi --memory-limit=0", + "test:types": "phpstan analyse --ansi --memory-limit=-1", "test:unit": "php bin/pest --colors=always --exclude-group=integration", "test:integration": "php bin/pest --colors=always --group=integration", "update:snapshots": "REBUILD_SNAPSHOTS=true php bin/pest --colors=always", diff --git a/src/Logging/TeamCity.php b/src/Logging/TeamCity.php index 97db03e0..41f54a24 100644 --- a/src/Logging/TeamCity.php +++ b/src/Logging/TeamCity.php @@ -38,14 +38,6 @@ final class TeamCity extends DefaultResultPrinter /** @var \PHPUnit\Util\Log\TeamCity */ private $phpunitTeamCity; - /** - * A stack of error messages and test failures to be displayed - * once the test suite has finished running. - * - * @var array - */ - protected $outputStack = []; - /** * @param resource|string|null $out */ @@ -73,10 +65,55 @@ final class TeamCity extends DefaultResultPrinter public function printResult(TestResult $result): void { - $this->printHeader($result); $this->printFooter($result); } + protected function printFooter(TestResult $result): void + { + $this->writeNewLine(); + $this->writeProgress('Tests: '); + + $results = [ + 'failed' => ['count' => $result->errorCount() + $result->failureCount(), 'color' => 'fg-red'], + 'skipped' => ['count' => $result->skippedCount(), 'color' => 'fg-cyan'], + 'warned' => ['count' => $result->warningCount(), 'color' => 'fg-cyan'], + 'risked' => ['count' => $result->riskyCount(), 'color' => 'fg-cyan'], + 'incomplete' => ['count' => $result->notImplementedCount(), 'color' => 'fg-cyan'], + 'passed' => ['count' => $this->successfulTestCount($result), 'color' => 'fg-green'], + ]; + + $filteredResults = array_filter($results, function ($item): bool { + return $item['count'] > 0; + }); + + foreach ($filteredResults as $key => $info) { + $this->writeProgressWithColor($info['color'], $info['count'] . " $key"); + + if ($key !== array_reverse(array_keys($filteredResults))[0]) { + $this->write(', '); + } + } + + $this->writeNewLine(); + $this->write("Assertions: $this->numAssertions"); + + $this->writeNewLine(); + $this->write("Time: {$result->time()}s"); + + $this->writeNewLine(); + } + + private function successfulTestCount(TestResult $result): int + { + return $result->count() + - $result->failureCount() + - $result->errorCount() + - $result->skippedCount() + - $result->warningCount() + - $result->notImplementedCount() + - $result->riskyCount(); + } + /** @phpstan-ignore-next-line */ public function startTestSuite(TestSuite $suite): void { @@ -115,143 +152,14 @@ final class TeamCity extends DefaultResultPrinter ]); } - /** @phpstan-ignore-next-line */ - public function endTestSuite(TestSuite $suite): void - { - $suiteName = $suite->getName(); - - if (static::isPestTestSuite($suite)) { - $this->writeNewLine(); - } - - if (file_exists($suiteName) || !method_exists($suiteName, '__getFileName')) { - $this->printEvent( - self::TEST_SUITE_FINISHED, [ - self::NAME => $suiteName, - self::LOCATION_HINT => self::PROTOCOL . $suiteName, - ]); - - return; - } - - $this->printEvent( - self::TEST_SUITE_FINISHED, [ - self::NAME => substr($suiteName, 2), - ]); - } - /** - * @param Test|Testable $test - */ - public function startTest(Test $test): void - { - if (!TeamCity::isPestTest($test)) { - $this->phpunitTeamCity->startTest($test); - - return; - } - - $this->printEvent('testStarted', [ - self::NAME => $test->getName(), - // @phpstan-ignore-next-line - self::LOCATION_HINT => self::PROTOCOL . $test->toString(), - ]); - } - - /** - * @param Test|Testable $test - */ - public function endTest(Test $test, float $time): void - { - if (!TeamCity::isPestTest($test)) { - $this->phpunitTeamCity->endTest($test, $time); - - return; - } - - if (!$this->lastTestFailed) { - $this->writePestTestOutput($test->getName(), 'fg-green, bold', '✓'); - } - - if ($test instanceof TestCase) { - $this->numAssertions += $test->getNumAssertions(); - } elseif ($test instanceof PhptTestCase) { - $this->numAssertions++; - } - - $this->lastTestFailed = false; - - $this->printEvent('testFinished', [ - self::NAME => $test->getName(), - self::DURATION => self::toMilliseconds($time), - ]); - } - - private function writePestTestOutput(string $message, string $color, string $symbol, string $suffix = null): void - { - $this->writeProgressWithColor($color, "$symbol "); - $this->writeProgress($message); - - if ($suffix !== null && strlen($suffix) > 0) { - $suffix = str_replace("\n", ' ', $suffix); - $this->writeWithColor($color, " -> $suffix"); - } - } - - /** - * @param Test|Testable $test - */ - public function addError(Test $test, Throwable $t, float $time): void - { - $this->lastTestFailed = true; - $this->writePestTestOutput($test->getName(), 'fg-red, bold', '⨯'); - - $this->outputStack[] = function () use ($test, $t, $time): void { - $this->writeNewLine(); - $this->writeWithColor('fg-red', "• {$test->getPrintableTestCaseName()} > {$test->getName()}"); - $this->phpunitTeamCity->addError($test, $t, $time); - }; - } - - public function addFailure(Test $test, AssertionFailedError $e, float $time): void - { - $this->lastTestFailed = true; - $this->writePestTestOutput($test->getName(), 'fg-red, bold', '⨯'); - - $this->outputStack[] = function () use ($test, $e, $time): void { - $this->writeNewLine(); - $this->writeWithColor('fg-red', "• {$test->getPrintableTestCaseName()} > {$test->getName()}"); - $this->phpunitTeamCity->addFailure($test, $e, $time); - }; - } - - /** - * @phpstan-ignore-next-line + * Verify that the given test suite is a valid Pest suite. * - * @param Test|Testable $test + * @param TestSuite $suite */ - public function addWarning(Test $test, Warning $e, float $time): void + private static function isPestTestSuite(TestSuite $suite): bool { - $this->lastTestFailed = true; - $this->writeWarning($test, $e); - } - - public function addIncompleteTest(Test $test, Throwable $t, float $time): void - { - $this->lastTestFailed = true; - $this->writeWarning($test, $t); - } - - public function addRiskyTest(Test $test, Throwable $t, float $time): void - { - $this->lastTestFailed = true; - $this->writeWarning($test, $t); - } - - public function addSkippedTest(Test $test, Throwable $t, float $time): void - { - $this->lastTestFailed = true; - $this->writeWarning($test, $t); + return strncmp($suite->getName(), 'P\\', strlen('P\\')) === 0; } /** @@ -273,64 +181,6 @@ final class TeamCity extends DefaultResultPrinter $this->write("]\n"); } - private function writeWarning(Test $test, Throwable $t): void - { - $this->writePestTestOutput($test->getName(), 'fg-cyan, bold', '-', $t->getMessage()); - } - - private function successfulTestCount(TestResult $result): int - { - return $result->count() - - $result->failureCount() - - $result->errorCount() - - $result->skippedCount() - - $result->warningCount() - - $result->notImplementedCount() - - $result->riskyCount(); - } - - protected function printHeader(TestResult $result): void - { - foreach ($this->outputStack as $callable) { - $callable(); - } - } - - protected function printFooter(TestResult $result): void - { - $this->writeNewLine(); - $this->writeProgress('Tests: '); - - $results = [ - 'failed' => ['count' => $result->errorCount() + $result->failureCount(), 'color' => 'fg-red'], - 'skipped' => ['count' => $result->skippedCount(), 'color' => 'fg-cyan'], - 'warned' => ['count' => $result->warningCount(), 'color' => 'fg-cyan'], - 'risked' => ['count' => $result->riskyCount(), 'color' => 'fg-cyan'], - 'incomplete' => ['count' => $result->notImplementedCount(), 'color' => 'fg-cyan'], - 'passed' => ['count' => $this->successfulTestCount($result), 'color' => 'fg-green'], - ]; - - $filteredResults = array_filter($results, function ($item): bool { - return $item['count'] > 0; - }); - - foreach ($filteredResults as $key => $info) { - $this->writeProgressWithColor($info['color'], $info['count'] . " $key"); - - if ($key !== array_reverse(array_keys($filteredResults))[0]) { - $this->write(', '); - } - } - - $this->writeNewLine(); - $this->write("Assertions: $this->numAssertions"); - - $this->writeNewLine(); - $this->write("Time: {$result->time()}s"); - - $this->writeNewLine(); - } - private static function escapeValue(string $text): string { return str_replace( @@ -340,9 +190,47 @@ final class TeamCity extends DefaultResultPrinter ); } - private static function toMilliseconds(float $time): int + /** @phpstan-ignore-next-line */ + public function endTestSuite(TestSuite $suite): void { - return (int) round($time * 1000); + $suiteName = $suite->getName(); + + if (static::isPestTestSuite($suite)) { + $this->writeNewLine(); + } + + if (file_exists($suiteName) || !method_exists($suiteName, '__getFileName')) { + $this->printEvent( + self::TEST_SUITE_FINISHED, [ + self::NAME => $suiteName, + self::LOCATION_HINT => self::PROTOCOL . $suiteName, + ]); + + return; + } + + $this->printEvent( + self::TEST_SUITE_FINISHED, [ + self::NAME => substr($suiteName, 2), + ]); + } + + /** + * @param Test|Testable $test + */ + public function startTest(Test $test): void + { + if (!TeamCity::isPestTest($test)) { + $this->phpunitTeamCity->startTest($test); + + return; + } + + $this->printEvent('testStarted', [ + self::NAME => $test->getName(), + // @phpstan-ignore-next-line + self::LOCATION_HINT => self::PROTOCOL . $test->toString(), + ]); } public static function isPestTest(Test $test): bool @@ -354,12 +242,101 @@ final class TeamCity extends DefaultResultPrinter } /** - * Verify that the given test suite is a valid Pest suite. - * - * @param TestSuite $suite + * @param Test|Testable $test */ - private static function isPestTestSuite(TestSuite $suite): bool + public function endTest(Test $test, float $time): void { - return strncmp($suite->getName(), 'P\\', strlen('P\\')) === 0; + if (!TeamCity::isPestTest($test)) { + $this->phpunitTeamCity->endTest($test, $time); + + return; + } + + $this->printEvent('testFinished', [ + self::NAME => $test->getName(), + self::DURATION => self::toMilliseconds($time), + ]); + + if (!$this->lastTestFailed) { + $this->writePestTestOutput($test->getName(), 'fg-green, bold', '✓'); + } + + if ($test instanceof TestCase) { + $this->numAssertions += $test->getNumAssertions(); + } elseif ($test instanceof PhptTestCase) { + $this->numAssertions++; + } + + $this->lastTestFailed = false; + } + + private function writePestTestOutput(string $message, string $color, string $symbol, string $suffix = null): void + { + $this->writeProgressWithColor($color, "$symbol "); + $this->writeProgress($message); + + if ($suffix !== null && strlen($suffix) > 0) { + $suffix = str_replace("\n", ' ', $suffix); + $this->writeWithColor($color, " -> $suffix"); + } + } + + private static function toMilliseconds(float $time): int + { + return (int) round($time * 1000); + } + + /** + * @param Test|Testable $test + */ + public function addError(Test $test, Throwable $t, float $time): void + { + $this->lastTestFailed = true; + $this->writePestTestOutput($test->getName(), 'fg-red, bold', '⨯'); + + $this->phpunitTeamCity->addError($test, $t, $time); + } + + public function addFailure(Test $test, AssertionFailedError $e, float $time): void + { + $this->lastTestFailed = true; + $this->writePestTestOutput($test->getName(), 'fg-red, bold', '⨯'); + + $this->phpunitTeamCity->addFailure($test, $e, $time); + } + + /** + * @phpstan-ignore-next-line + * + * @param Test|Testable $test + */ + public function addWarning(Test $test, Warning $e, float $time): void + { + $this->lastTestFailed = true; + $this->writeWarning($test, $e); + } + + private function writeWarning(Test $test, Throwable $t): void + { + $this->writePestTestOutput($test->getName(), 'fg-yellow, bold', '-', $t->getMessage()); + } + + public function addIncompleteTest(Test $test, Throwable $t, float $time): void + { + $this->lastTestFailed = true; + $this->writeWarning($test, $t); + } + + public function addRiskyTest(Test $test, Throwable $t, float $time): void + { + $this->lastTestFailed = true; + $this->writeWarning($test, $t); + } + + public function addSkippedTest(Test $test, Throwable $t, float $time): void + { + $this->lastTestFailed = true; + $this->writeWarning($test, $t); + $this->phpunitTeamCity->printIgnoredTest($test->getName(), $t, $time); } } diff --git a/tests/.snapshots/success.txt b/tests/.snapshots/success.txt index 0a23a6e9..fa2b86fa 100644 --- a/tests/.snapshots/success.txt +++ b/tests/.snapshots/success.txt @@ -578,6 +578,9 @@ PASS Tests\Visual\Help ✓ visual snapshot of help command output + PASS Tests\Visual\JUnit + ✓ it is can successfully call all public methods + PASS Tests\Visual\SingleTestOrDirectory ✓ allows to run a single test ✓ allows to run a directory @@ -587,6 +590,9 @@ WARN Tests\Visual\Success - visual snapshot of test suite on success + PASS Tests\Visual\TeamCity + ✓ it is can successfully call all public methods + PASS Tests\Features\Depends ✓ first ✓ second @@ -601,5 +607,5 @@ ✓ it is a test ✓ it uses correct parent class - Tests: 4 incompleted, 9 skipped, 381 passed + Tests: 4 incompleted, 9 skipped, 383 passed \ No newline at end of file