diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index f2947bfff..8b16ef572 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -10,6 +10,41 @@ parameters: count: 1 path: src/Collector/DefaultCollector.php + - + message: "#^Method Ergebnis\\\\PHPUnit\\\\SlowTestDetector\\\\Reporter\\\\Console\\\\ConsolePrinter\\:\\:printLine\\(\\) has no return type specified\\.$#" + count: 1 + path: src/Reporter/Console/ConsolePrinter.php + + - + message: "#^Method Ergebnis\\\\PHPUnit\\\\SlowTestDetector\\\\Reporter\\\\Console\\\\ConsoleReporter\\:\\:printFooter\\(\\) has no return type specified\\.$#" + count: 1 + path: src/Reporter/Console/ConsoleReporter.php + + - + message: "#^Method Ergebnis\\\\PHPUnit\\\\SlowTestDetector\\\\Reporter\\\\Console\\\\ConsoleReporter\\:\\:printLegend\\(\\) has no return type specified\\.$#" + count: 1 + path: src/Reporter/Console/ConsoleReporter.php + + - + message: "#^Method Ergebnis\\\\PHPUnit\\\\SlowTestDetector\\\\Reporter\\\\Console\\\\ConsoleReporter\\:\\:report\\(\\) has no return type specified\\.$#" + count: 1 + path: src/Reporter/Console/ConsoleReporter.php + + - + message: "#^Method Ergebnis\\\\PHPUnit\\\\SlowTestDetector\\\\Reporter\\\\Console\\\\ConsoleReporter\\:\\:reportWithCustomAndGlobalMaximumDuration\\(\\) has no return type specified\\.$#" + count: 1 + path: src/Reporter/Console/ConsoleReporter.php + + - + message: "#^Method Ergebnis\\\\PHPUnit\\\\SlowTestDetector\\\\Reporter\\\\Console\\\\ConsoleReporter\\:\\:reportWithGlobalMaximumDuration\\(\\) has no return type specified\\.$#" + count: 1 + path: src/Reporter/Console/ConsoleReporter.php + + - + message: "#^Method Ergebnis\\\\PHPUnit\\\\SlowTestDetector\\\\Reporter\\\\Reporter\\:\\:report\\(\\) has no return type specified\\.$#" + count: 1 + path: src/Reporter/Reporter.php + - message: "#^Offset 'major' does not exist on array\\{0\\?\\: string, major\\?\\: numeric\\-string, 1\\?\\: numeric\\-string, 2\\?\\: '0'\\|\\(non\\-falsy\\-string&numeric\\-string\\), minor\\?\\: ''\\|numeric\\-string, 3\\?\\: ''\\|numeric\\-string, 4\\?\\: '0'\\|\\(non\\-falsy\\-string&numeric\\-string\\)\\}\\.$#" count: 1 @@ -240,6 +275,11 @@ parameters: count: 1 path: test/Unit/Exception/InvalidNanosecondsTest.php + - + message: "#^Method Ergebnis\\\\PHPUnit\\\\SlowTestDetector\\\\Test\\\\Unit\\\\Exception\\\\InvalidOutputTest\\:\\:testNotResourceReturnsException\\(\\) has no return type specified\\.$#" + count: 1 + path: test/Unit/Exception/InvalidOutputTest.php + - message: "#^Method Ergebnis\\\\PHPUnit\\\\SlowTestDetector\\\\Test\\\\Unit\\\\Exception\\\\InvalidPhaseIdentifierTest\\:\\:testBlankOrEmptyReturnsException\\(\\) has no return type specified\\.$#" count: 1 @@ -336,15 +376,50 @@ parameters: path: test/Unit/Reporter/Console/ColorTest.php - - message: "#^Method Ergebnis\\\\PHPUnit\\\\SlowTestDetector\\\\Test\\\\Unit\\\\Reporter\\\\Console\\\\ConsoleReporterTest\\:\\:testReportReturnsEmptyStringWhenSlowTestListIsEmpty\\(\\) has no return type specified\\.$#" + message: "#^Method Ergebnis\\\\PHPUnit\\\\SlowTestDetector\\\\Test\\\\Unit\\\\Reporter\\\\Console\\\\ConsolePrinterTest\\:\\:testPrintLineWritesLineFollowedByNewlineToResource\\(\\) has no return type specified\\.$#" + count: 1 + path: test/Unit/Reporter/Console/ConsolePrinterTest.php + + - + message: "#^Parameter \\#1 \\$fp of function rewind expects resource, resource\\|false given\\.$#" + count: 1 + path: test/Unit/Reporter/Console/ConsolePrinterTest.php + + - + message: "#^Parameter \\#1 \\$output of class Ergebnis\\\\PHPUnit\\\\SlowTestDetector\\\\Reporter\\\\Console\\\\ConsolePrinter constructor expects resource, resource\\|false given\\.$#" + count: 1 + path: test/Unit/Reporter/Console/ConsolePrinterTest.php + + - + message: "#^Parameter \\#1 \\$source of function stream_get_contents expects resource, resource\\|false given\\.$#" + count: 1 + path: test/Unit/Reporter/Console/ConsolePrinterTest.php + + - + message: "#^Method Ergebnis\\\\PHPUnit\\\\SlowTestDetector\\\\Test\\\\Unit\\\\Reporter\\\\Console\\\\ConsoleReporterTest\\:\\:testReportPrintsNothingWhenSlowTestListIsEmpty\\(\\) has no return type specified\\.$#" count: 1 path: test/Unit/Reporter/Console/ConsoleReporterTest.php - - message: "#^Method Ergebnis\\\\PHPUnit\\\\SlowTestDetector\\\\Test\\\\Unit\\\\Reporter\\\\Console\\\\ConsoleReporterTest\\:\\:testReportReturnsReportWhenSlowTestListHasFewerSlowTestsThanMaximumCount\\(\\) has no return type specified\\.$#" + message: "#^Method Ergebnis\\\\PHPUnit\\\\SlowTestDetector\\\\Test\\\\Unit\\\\Reporter\\\\Console\\\\ConsoleReporterTest\\:\\:testReportPrintsReportWhenSlowTestListHasFewerSlowTestsThanMaximumCount\\(\\) has no return type specified\\.$#" count: 1 path: test/Unit/Reporter/Console/ConsoleReporterTest.php + - + message: "#^Parameter \\#1 \\$fp of function rewind expects resource, resource\\|false given\\.$#" + count: 2 + path: test/Unit/Reporter/Console/ConsoleReporterTest.php + + - + message: "#^Parameter \\#1 \\$output of class Ergebnis\\\\PHPUnit\\\\SlowTestDetector\\\\Reporter\\\\Console\\\\ConsolePrinter constructor expects resource, resource\\|false given\\.$#" + count: 2 + path: test/Unit/Reporter/Console/ConsoleReporterTest.php + + - + message: "#^Parameter \\#1 \\$source of function stream_get_contents expects resource, resource\\|false given\\.$#" + count: 2 + path: test/Unit/Reporter/Console/ConsoleReporterTest.php + - message: "#^Method Ergebnis\\\\PHPUnit\\\\SlowTestDetector\\\\Test\\\\Unit\\\\Reporter\\\\Console\\\\DurationFormatterTest\\:\\:testFormatFormats\\(\\) has no return type specified\\.$#" count: 1 diff --git a/src/Exception/InvalidOutput.php b/src/Exception/InvalidOutput.php new file mode 100644 index 000000000..a5107b8bc --- /dev/null +++ b/src/Exception/InvalidOutput.php @@ -0,0 +1,31 @@ +maximumDuration = $maximumDuration; $this->collector = new Collector\DefaultCollector(); $this->reporter = new Reporter\Console\ConsoleReporter( + new Reporter\Console\ConsolePrinter(\fopen( + 'php://stdout', + 'wb' + )), new Reporter\Console\DurationFormatter(), $maximumDuration, $maximumCount @@ -135,13 +139,7 @@ public function endTestSuite(Framework\TestSuite $suite) return; } - $report = $this->reporter->report($slowTestList); - - if ('' === $report) { - return; - } - - echo $report; + $this->reporter->report($slowTestList); } public function startTest(Framework\Test $test) @@ -272,6 +270,10 @@ public function __construct(array $options = []) $this->maximumDuration = $maximumDuration; $this->collector = new Collector\DefaultCollector(); $this->reporter = new Reporter\Console\ConsoleReporter( + new Reporter\Console\ConsolePrinter(\fopen( + 'php://stdout', + 'wb' + )), new Reporter\Console\DurationFormatter(), $maximumDuration, $maximumCount @@ -337,13 +339,7 @@ public function executeAfterLastTest(): void return; } - $report = $this->reporter->report($slowTestList); - - if ('' === $report) { - return; - } - - echo $report; + $this->reporter->report($slowTestList); } private function resolveMaximumDuration(string $test): MaximumDuration @@ -465,11 +461,11 @@ public function bootstrap( new Subscriber\TestRunner\ExecutionFinishedSubscriber( $collector, new Reporter\Console\ConsoleReporter( + new Reporter\Console\ConsolePrinter($output), new Reporter\Console\DurationFormatter(), $maximumDuration, $maximumCount - ), - $output + ) ) ); } diff --git a/src/Reporter/Console/ConsolePrinter.php b/src/Reporter/Console/ConsolePrinter.php new file mode 100644 index 000000000..5c9e36867 --- /dev/null +++ b/src/Reporter/Console/ConsolePrinter.php @@ -0,0 +1,49 @@ +output = $output; + } + + public function printLine(string $line) + { + \fwrite( + $this->output, + $line . "\n" + ); + } +} diff --git a/src/Reporter/Console/ConsoleReporter.php b/src/Reporter/Console/ConsoleReporter.php index b7e3cda3c..e7577c33e 100644 --- a/src/Reporter/Console/ConsoleReporter.php +++ b/src/Reporter/Console/ConsoleReporter.php @@ -26,6 +26,11 @@ */ final class ConsoleReporter implements Reporter\Reporter { + /** + * @var ConsolePrinter + */ + private $printer; + /** * @var DurationFormatter */ @@ -42,36 +47,18 @@ final class ConsoleReporter implements Reporter\Reporter private $maximumCount; public function __construct( + ConsolePrinter $printer, DurationFormatter $durationFormatter, MaximumDuration $maximumDuration, MaximumCount $maximumCount ) { + $this->printer = $printer; $this->durationFormatter = $durationFormatter; $this->maximumDuration = $maximumDuration; $this->maximumCount = $maximumCount; } - public function report(SlowTestList $slowTestList): string - { - $lines = \iterator_to_array( - $this->lines($slowTestList), - false - ); - - if ([] === $lines) { - return ''; - } - - return \implode( - "\n", - $lines - ); - } - - /** - * @return \Generator - */ - private function lines(SlowTestList $slowTestList): \Generator + public function report(SlowTestList $slowTestList) { $slowTestCount = $slowTestList->count(); @@ -84,7 +71,7 @@ private function lines(SlowTestList $slowTestList): \Generator ->limitTo($this->maximumCount); if ($slowTestListThatWillBeReported->hasSlowTestWithMaximumDurationDifferentFrom($this->maximumDuration->toDuration())) { - yield from $this->reportWithCustomAndGlobalMaximumDuration( + $this->reportWithCustomAndGlobalMaximumDuration( $slowTestCount, $slowTestListThatWillBeReported ); @@ -92,22 +79,19 @@ private function lines(SlowTestList $slowTestList): \Generator return; } - yield from $this->reportWithGlobalMaximumDuration( + $this->reportWithGlobalMaximumDuration( $slowTestCount, $slowTestListThatWillBeReported ); } - /** - * @return \Generator - */ private function reportWithCustomAndGlobalMaximumDuration( Count $slowTestCount, SlowTestList $slowTestListThatWillBeReported - ): \Generator { - yield ''; + ) { + $this->printer->printLine(''); - yield ''; + $this->printer->printLine(''); $unit = Unit::fromDurations( $this->maximumDuration->toDuration(), @@ -126,14 +110,14 @@ private function reportWithCustomAndGlobalMaximumDuration( $this->maximumDuration->toDuration() ); - yield \sprintf( + $this->printer->printLine(\sprintf( 'Detected %d %s where the duration exceeded a custom or the global maximum duration (%s).', $slowTestCount->toInt(), $slowTestCount->equals(Count::fromInt(1)) ? 'test' : 'tests', $globalMaximumDurationFormatted - ); + )); - yield ''; + $this->printer->printLine(''); $numberColumnWidth = \strlen((string) $slowTestListThatWillBeReported->count()->toInt()); $durationColumnWidth = $this->durationColumnWidth( @@ -156,12 +140,12 @@ private function reportWithCustomAndGlobalMaximumDuration( $durationColumnWidth + 1 + $durationColumnWidth ); - yield \sprintf( + $this->printer->printLine(\sprintf( $headerTemplate, '#', 'Duration', 'Test' - ); + )); $subHeaderTemplate = \sprintf( '%%%ds %%-%ds %%s', @@ -169,19 +153,19 @@ private function reportWithCustomAndGlobalMaximumDuration( $durationColumnWidth ); - yield \sprintf( + $this->printer->printLine(\sprintf( $subHeaderTemplate, '', 'Actual', 'Maximum' - ); + )); $separator = \str_repeat( '-', $numberColumnWidth + 1 + $durationColumnWidth + 1 + $durationColumnWidth + 1 + $testDescriptionColumnWidth ); - yield $separator; + $this->printer->printLine($separator); $rowTemplate = \sprintf( '%%%dd %%%ds %%%ds %%s', @@ -207,36 +191,33 @@ private function reportWithCustomAndGlobalMaximumDuration( ); } - yield \sprintf( + $this->printer->printLine(\sprintf( $rowTemplate, $i + 1, $actualDurationFormatted, $maximumDurationFormatted, $slowTest->testDescription()->toString() - ); + )); } - yield $separator; + $this->printer->printLine($separator); - yield from $this->legend( + $this->printLegend( $unit, $numberColumnWidth + 1, $durationColumnWidth ); - yield from $this->footer($slowTestCount); + $this->printFooter($slowTestCount); } - /** - * @return \Generator - */ private function reportWithGlobalMaximumDuration( Count $slowTestCount, SlowTestList $slowTestListThatWillBeReported - ): \Generator { - yield ''; + ) { + $this->printer->printLine(''); - yield ''; + $this->printer->printLine(''); $unit = Unit::fromDurations( $this->maximumDuration->toDuration(), @@ -250,14 +231,14 @@ private function reportWithGlobalMaximumDuration( $this->maximumDuration->toDuration() ); - yield \sprintf( + $this->printer->printLine(\sprintf( 'Detected %d %s where the duration exceeded the global maximum duration (%s).', $slowTestCount->toInt(), $slowTestCount->equals(Count::fromInt(1)) ? 'test' : 'tests', $globalMaximumDurationFormatted - ); + )); - yield ''; + $this->printer->printLine(''); $numberColumnWidth = \strlen((string) $slowTestListThatWillBeReported->count()->toInt()); $durationColumnWidth = $this->durationColumnWidth( @@ -275,19 +256,19 @@ private function reportWithGlobalMaximumDuration( $durationColumnWidth ); - yield \sprintf( + $this->printer->printLine(\sprintf( $headerTemplate, '#', 'Duration', 'Test' - ); + )); $separator = \str_repeat( '-', $numberColumnWidth + 1 + $durationColumnWidth + 1 + $testDescriptionColumnWidth ); - yield $separator; + $this->printer->printLine($separator); $rowTemplate = \sprintf( '%%%dd %%%ds %%s', @@ -301,23 +282,23 @@ private function reportWithGlobalMaximumDuration( $slowTest->duration() ); - yield \sprintf( + $this->printer->printLine(\sprintf( $rowTemplate, $i + 1, $durationFormatted, $slowTest->testDescription()->toString() - ); + )); } - yield $separator; + $this->printer->printLine($separator); - yield from $this->legend( + $this->printLegend( $unit, $numberColumnWidth + 1, $durationColumnWidth ); - yield from $this->footer($slowTestCount); + $this->printFooter($slowTestCount); } private function durationColumnWidth( @@ -335,14 +316,11 @@ private function durationColumnWidth( ); } - /** - * @return \Generator - */ - private function legend( + private function printLegend( Unit $unit, int $columnStart, int $columnWidth - ): \Generator { + ) { $durationOfZero = Duration::fromSecondsAndNanoseconds( 0, 0 @@ -358,33 +336,30 @@ private function legend( $columnStart + $columnWidth - \strlen($durationOfZeroFormatted) ); - yield $padding . $durationOfZeroFormatted; + $this->printer->printLine($padding . $durationOfZeroFormatted); if ($unit->equals(Unit::hours())) { - yield $padding . ' │ │ └─── seconds'; + $this->printer->printLine($padding . ' │ │ └─── seconds'); - yield $padding . ' │ └────── minutes'; + $this->printer->printLine($padding . ' │ └────── minutes'); - yield $padding . ' └───────── hours'; + $this->printer->printLine($padding . ' └───────── hours'); return; } if ($unit->equals(Unit::minutes())) { - yield $padding . ' │ └─── seconds'; + $this->printer->printLine($padding . ' │ └─── seconds'); - yield $padding . ' └────── minutes'; + $this->printer->printLine($padding . ' └────── minutes'); return; } - yield $padding . ' └─── seconds'; + $this->printer->printLine($padding . ' └─── seconds'); } - /** - * @return \Generator - */ - private function footer(Count $slowTestCount): \Generator + private function printFooter(Count $slowTestCount) { $additionalSlowTestCount = Count::fromInt(\max( 0, @@ -395,15 +370,17 @@ private function footer(Count $slowTestCount): \Generator return; } - yield ''; + $this->printer->printLine(''); if ($additionalSlowTestCount->equals(Count::fromInt(1))) { - yield 'There is 1 additional slow test that is not listed here.'; - } else { - yield \sprintf( - 'There are %d additional slow tests that are not listed here.', - $additionalSlowTestCount->toInt() - ); + $this->printer->printLine('There is 1 additional slow test that is not listed here.'); + + return; } + + $this->printer->printLine(\sprintf( + 'There are %d additional slow tests that are not listed here.', + $additionalSlowTestCount->toInt() + )); } } diff --git a/src/Reporter/Reporter.php b/src/Reporter/Reporter.php index e6c84c4a2..9e522504b 100644 --- a/src/Reporter/Reporter.php +++ b/src/Reporter/Reporter.php @@ -20,5 +20,5 @@ */ interface Reporter { - public function report(SlowTestList $slowTestList): string; + public function report(SlowTestList $slowTestList); } diff --git a/src/Subscriber/TestRunner/ExecutionFinishedSubscriber.php b/src/Subscriber/TestRunner/ExecutionFinishedSubscriber.php index 22c58988c..66def6fad 100644 --- a/src/Subscriber/TestRunner/ExecutionFinishedSubscriber.php +++ b/src/Subscriber/TestRunner/ExecutionFinishedSubscriber.php @@ -32,22 +32,12 @@ final class ExecutionFinishedSubscriber implements Event\TestRunner\ExecutionFin */ private $reporter; - /** - * @var resource - */ - private $output; - - /** - * @param resource $output - */ public function __construct( Collector\Collector $collector, - Reporter\Reporter $reporter, - $output + Reporter\Reporter $reporter ) { $this->collector = $collector; $this->reporter = $reporter; - $this->output = $output; } /** @@ -61,15 +51,6 @@ public function notify(Event\TestRunner\ExecutionFinished $event): void return; } - $report = $this->reporter->report($slowTestList); - - if ('' === $report) { - return; - } - - \fwrite( - $this->output, - $report - ); + $this->reporter->report($slowTestList); } } diff --git a/test/Unit/Exception/InvalidOutputTest.php b/test/Unit/Exception/InvalidOutputTest.php new file mode 100644 index 000000000..ea1d18b49 --- /dev/null +++ b/test/Unit/Exception/InvalidOutputTest.php @@ -0,0 +1,66 @@ +getMessage()); + } + + /** + * @return \Generator + */ + public static function provideOutput(): iterable + { + $faker = self::faker(); + + $values = [ + 'boolean' => $faker->boolean(), + 'integer' => $faker->numberBetween(), + 'string' => $faker->sentence(), + 'array' => $faker->words(), + 'null' => null, + 'object' => new \stdClass(), + ]; + + foreach ($values as $key => $output) { + yield $key => [ + $output, + ]; + } + } +} diff --git a/test/Unit/Reporter/Console/ConsolePrinterTest.php b/test/Unit/Reporter/Console/ConsolePrinterTest.php new file mode 100644 index 000000000..f3c4e7a03 --- /dev/null +++ b/test/Unit/Reporter/Console/ConsolePrinterTest.php @@ -0,0 +1,49 @@ +sentence(); + + $output = \fopen( + 'php://memory', + 'rb+' + ); + + $printer = new Reporter\Console\ConsolePrinter($output); + + $printer->printLine($line); + + \rewind($output); + + $expectedOutput = \sprintf( + "%s\n", + $line + ); + + self::assertSame($expectedOutput, \stream_get_contents($output)); + } +} diff --git a/test/Unit/Reporter/Console/ConsoleReporterTest.php b/test/Unit/Reporter/Console/ConsoleReporterTest.php index 281c27c82..a132bdec8 100644 --- a/test/Unit/Reporter/Console/ConsoleReporterTest.php +++ b/test/Unit/Reporter/Console/ConsoleReporterTest.php @@ -33,6 +33,7 @@ * @uses \Ergebnis\PHPUnit\SlowTestDetector\Duration * @uses \Ergebnis\PHPUnit\SlowTestDetector\MaximumCount * @uses \Ergebnis\PHPUnit\SlowTestDetector\MaximumDuration + * @uses \Ergebnis\PHPUnit\SlowTestDetector\Reporter\Console\ConsolePrinter * @uses \Ergebnis\PHPUnit\SlowTestDetector\Reporter\Console\DurationFormatter * @uses \Ergebnis\PHPUnit\SlowTestDetector\Reporter\Console\Unit * @uses \Ergebnis\PHPUnit\SlowTestDetector\SlowTest @@ -44,41 +45,57 @@ final class ConsoleReporterTest extends Framework\TestCase { use Test\Util\Helper; - public function testReportReturnsEmptyStringWhenSlowTestListIsEmpty() + public function testReportPrintsNothingWhenSlowTestListIsEmpty() { $faker = self::faker(); $slowTestList = SlowTestList::create(); + $output = \fopen( + 'php://memory', + 'rb+' + ); + $reporter = new Reporter\Console\ConsoleReporter( + new Reporter\Console\ConsolePrinter($output), new Reporter\Console\DurationFormatter(), MaximumDuration::default(), MaximumCount::fromCount(Count::fromInt($faker->numberBetween(1))) ); - $report = $reporter->report($slowTestList); + $reporter->report($slowTestList); + + \rewind($output); - self::assertSame('', $report); + self::assertSame('', \stream_get_contents($output)); } /** * @dataProvider provideExpectedReportMaximumDurationMaximumCountAndSlowTestList */ - public function testReportReturnsReportWhenSlowTestListHasFewerSlowTestsThanMaximumCount( + public function testReportPrintsReportWhenSlowTestListHasFewerSlowTestsThanMaximumCount( string $expectedReport, MaximumDuration $maximumDuration, MaximumCount $maximumCount, SlowTestList $slowTestList ) { + $output = \fopen( + 'php://memory', + 'rb+' + ); + $reporter = new Reporter\Console\ConsoleReporter( + new Reporter\Console\ConsolePrinter($output), new Reporter\Console\DurationFormatter(), $maximumDuration, $maximumCount ); - $report = $reporter->report($slowTestList); + $reporter->report($slowTestList); + + \rewind($output); - self::assertSame($expectedReport, $report); + self::assertSame($expectedReport, \stream_get_contents($output)); } /** @@ -88,8 +105,10 @@ public static function provideExpectedReportMaximumDurationMaximumCountAndSlowTe { $print = static function (array $lines): string { return \implode( - "\n", - $lines + '', + \array_map(static function (string $line): string { + return $line . "\n"; + }, $lines) ); };