diff --git a/.github/workflows/code_analysis.yaml b/.github/workflows/code_analysis.yaml index d4869162c92..33831a4260f 100644 --- a/.github/workflows/code_analysis.yaml +++ b/.github/workflows/code_analysis.yaml @@ -53,7 +53,7 @@ jobs: - name: 'Active Classes' run: | - vendor/bin/class-leak check bin config src rules utils --skip-suffix "Rector" --skip-type="Rector\\Utils\\Compiler\\Unprefixer" --skip-type="Rector\\NodeCollector\\BinaryOpConditionsCollector" --skip-type="Rector\\Set\\Contract\\SetListInterface" --skip-type="Rector\\DependencyInjection\\PHPStan\\RichParserFactory" --skip-type="Rector\\Utils\\PHPStan\\Tests\\Rule\\SingleServiceRegistrationRule\\Source\\DuplicateRegistrationFactory" --skip-type="Rector\\Utils\\PHPStan\\Tests\\Rule\\SingleServiceRegistrationRule\\Source\\SingleRegistrationFactory" --skip-type="Rector\\ChangesReporting\\Contract\\Output\\OutputFormatterInterface" --skip-type="Rector\\NodeTypeResolver\\Contract\\NodeTypeResolverInterface" --skip-type="Rector\\CodingStyle\\Contract\\ClassNameImport\\ClassNameImportSkipVoterInterface" --skip-type="Rector\\Php80\\Contract\\ConverterAttributeDecoratorInterface" --skip-type="Rector\\NodeNameResolver\\Contract\\NodeNameResolverInterface" --skip-type="Rector\\StaticTypeMapper\\Contract\\PhpDocParser\\PhpDocTypeMapperInterface" --skip-type="Rector\\PhpAttribute\\Contract\\AnnotationToAttributeMapperInterface" --skip-type="Rector\\PHPStanStaticTypeMapper\\Contract\\TypeMapperInterface" --skip-type="Rector\\StaticTypeMapper\\Contract\\PhpParser\\PhpParserNodeMapperInterface" + vendor/bin/class-leak check bin config src rules utils --skip-suffix "Rector" --skip-type="Rector\\Utils\\Compiler\\Unprefixer" --skip-type="Rector\\NodeCollector\\BinaryOpConditionsCollector" --skip-type="Rector\\Set\\Contract\\SetListInterface" --skip-type="Rector\\DependencyInjection\\PHPStan\\RichParserFactory" --skip-type="Rector\\Utils\\PHPStan\\Tests\\Rule\\SingleServiceRegistrationRule\\Source\\DuplicateRegistrationFactory" --skip-type="Rector\\Utils\\PHPStan\\Tests\\Rule\\SingleServiceRegistrationRule\\Source\\SingleRegistrationFactory" --skip-type="Rector\\ChangesReporting\\Contract\\Output\\OutputFormatterInterface" --skip-type="Rector\\NodeTypeResolver\\Contract\\NodeTypeResolverInterface" --skip-type="Rector\\CodingStyle\\Contract\\ClassNameImport\\ClassNameImportSkipVoterInterface" --skip-type="Rector\\Php80\\Contract\\ConverterAttributeDecoratorInterface" --skip-type="Rector\\NodeNameResolver\\Contract\\NodeNameResolverInterface" --skip-type="Rector\\StaticTypeMapper\\Contract\\PhpDocParser\\PhpDocTypeMapperInterface" --skip-type="Rector\\PhpAttribute\\Contract\\AnnotationToAttributeMapperInterface" --skip-type="Rector\\PHPStanStaticTypeMapper\\Contract\\TypeMapperInterface" --skip-type="Rector\\StaticTypeMapper\\Contract\\PhpParser\\PhpParserNodeMapperInterface" --skip-type="Rector\\Utils\\PHPUnit\\SlowestTests\\SlowTestExtension" - name: 'Compatible PHPStan versions' diff --git a/phpunit.xml b/phpunit.xml index 37c4eb12d7f..c134897098b 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -21,4 +21,8 @@ + + + + diff --git a/utils/PHPUnit/SlowestTests/RunnerFinishedSubscriber.php b/utils/PHPUnit/SlowestTests/RunnerFinishedSubscriber.php new file mode 100644 index 00000000000..42452ea0809 --- /dev/null +++ b/utils/PHPUnit/SlowestTests/RunnerFinishedSubscriber.php @@ -0,0 +1,21 @@ +slowTestReporter->printSlowest(); + } +} diff --git a/utils/PHPUnit/SlowestTests/SlowTestExtension.php b/utils/PHPUnit/SlowestTests/SlowTestExtension.php new file mode 100644 index 00000000000..fde3cf46b27 --- /dev/null +++ b/utils/PHPUnit/SlowestTests/SlowTestExtension.php @@ -0,0 +1,24 @@ +registerSubscribers( + new TestPreparedSubscriber($slowTestReporter), + new TestFinishedSubscriber($slowTestReporter), + new RunnerFinishedSubscriber($slowTestReporter), + ); + } +} diff --git a/utils/PHPUnit/SlowestTests/SlowTestReporter.php b/utils/PHPUnit/SlowestTests/SlowTestReporter.php new file mode 100644 index 00000000000..8e7e0cb8c7a --- /dev/null +++ b/utils/PHPUnit/SlowestTests/SlowTestReporter.php @@ -0,0 +1,67 @@ + + */ + private array $startedAt = []; + + /** + * Test name => accumulated duration in seconds (summed across data-provider fixtures). + * @var array + */ + private array $durations = []; + + public function testPrepared(string $testId, float $secondsSinceStart): void + { + $this->startedAt[$testId] = $secondsSinceStart; + } + + public function testFinished(string $testId, string $testName, float $secondsSinceStart): void + { + if (! isset($this->startedAt[$testId])) { + return; + } + + $this->durations[$testName] = ($this->durations[$testName] ?? 0.0) + ($secondsSinceStart - $this->startedAt[$testId]); + unset($this->startedAt[$testId]); + } + + public function printSlowest(): void + { + $slowDurations = array_filter( + $this->durations, + static fn (float $seconds): bool => $seconds >= self::THRESHOLD_SECONDS + ); + + if ($slowDurations === []) { + return; + } + + arsort($slowDurations); + $slowDurations = array_slice($slowDurations, 0, self::LIMIT, true); + + echo PHP_EOL . sprintf('%d slowest tests (>= %.1fs):', count($slowDurations), self::THRESHOLD_SECONDS) . PHP_EOL; + + $position = 1; + foreach ($slowDurations as $testName => $seconds) { + echo sprintf('%3d. %6.2fs %s', $position, $seconds, $testName) . PHP_EOL; + ++$position; + } + + echo PHP_EOL; + } +} diff --git a/utils/PHPUnit/SlowestTests/TestFinishedSubscriber.php b/utils/PHPUnit/SlowestTests/TestFinishedSubscriber.php new file mode 100644 index 00000000000..79dc10d26d1 --- /dev/null +++ b/utils/PHPUnit/SlowestTests/TestFinishedSubscriber.php @@ -0,0 +1,31 @@ +test(); + + // group all data-provider fixtures under one test method, so a slow rule shows as a single entry + $reportName = $test instanceof TestMethod ? $test->className() . '::' . $test->methodName() : $test->name(); + + $this->slowTestReporter->testFinished( + $test->id(), + $reportName, + $event->telemetryInfo()->durationSinceStart()->asFloat() + ); + } +} diff --git a/utils/PHPUnit/SlowestTests/TestPreparedSubscriber.php b/utils/PHPUnit/SlowestTests/TestPreparedSubscriber.php new file mode 100644 index 00000000000..49426b9de78 --- /dev/null +++ b/utils/PHPUnit/SlowestTests/TestPreparedSubscriber.php @@ -0,0 +1,24 @@ +slowTestReporter->testPrepared( + $event->test()->id(), + $event->telemetryInfo()->durationSinceStart()->asFloat() + ); + } +}