diff --git a/src/Symfony/Bridge/PhpUnit/CoverageListener.php b/src/Symfony/Bridge/PhpUnit/CoverageListener.php index 5dd29a9cbb..766252b872 100644 --- a/src/Symfony/Bridge/PhpUnit/CoverageListener.php +++ b/src/Symfony/Bridge/PhpUnit/CoverageListener.php @@ -11,10 +11,109 @@ namespace Symfony\Bridge\PhpUnit; -class_alias('Symfony\Bridge\PhpUnit\Legacy\CoverageListenerForV7', 'Symfony\Bridge\PhpUnit\CoverageListener'); +use PHPUnit\Framework\Test; +use PHPUnit\Framework\TestCase; +use PHPUnit\Framework\TestListener; +use PHPUnit\Framework\TestListenerDefaultImplementation; +use PHPUnit\Framework\Warning; +use PHPUnit\Util\Annotation\Registry; +use PHPUnit\Util\Test as TestUtil; -if (false) { - class CoverageListener +class CoverageListener implements TestListener +{ + use TestListenerDefaultImplementation; + + private $sutFqcnResolver; + private $warningOnSutNotFound; + + public function __construct(callable $sutFqcnResolver = null, bool $warningOnSutNotFound = false) { + $this->sutFqcnResolver = $sutFqcnResolver ?? static function (Test $test): ?string { + $class = \get_class($test); + + $sutFqcn = str_replace('\\Tests\\', '\\', $class); + $sutFqcn = preg_replace('{Test$}', '', $sutFqcn); + + return class_exists($sutFqcn) ? $sutFqcn : null; + }; + + $this->warningOnSutNotFound = $warningOnSutNotFound; + } + + public function startTest(Test $test): void + { + if (!$test instanceof TestCase) { + return; + } + + $annotations = TestUtil::parseTestMethodAnnotations(\get_class($test), $test->getName(false)); + + $ignoredAnnotations = ['covers', 'coversDefaultClass', 'coversNothing']; + + foreach ($ignoredAnnotations as $annotation) { + if (isset($annotations['class'][$annotation]) || isset($annotations['method'][$annotation])) { + return; + } + } + + $sutFqcn = ($this->sutFqcnResolver)($test); + if (!$sutFqcn) { + if ($this->warningOnSutNotFound) { + $test->getTestResultObject()->addWarning($test, new Warning('Could not find the tested class.'), 0); + } + + return; + } + + $covers = $sutFqcn; + if (!\is_array($sutFqcn)) { + $covers = [$sutFqcn]; + while ($parent = get_parent_class($sutFqcn)) { + $covers[] = $parent; + $sutFqcn = $parent; + } + } + + if (class_exists(Registry::class)) { + $this->addCoversForDocBlockInsideRegistry($test, $covers); + + return; + } + + $this->addCoversForClassToAnnotationCache($test, $covers); + } + + private function addCoversForClassToAnnotationCache(Test $test, array $covers): void + { + $r = new \ReflectionProperty(TestUtil::class, 'annotationCache'); + $r->setAccessible(true); + + $cache = $r->getValue(); + $cache = array_replace_recursive($cache, [ + \get_class($test) => [ + 'covers' => $covers, + ], + ]); + + $r->setValue(TestUtil::class, $cache); + } + + private function addCoversForDocBlockInsideRegistry(Test $test, array $covers): void + { + $docBlock = Registry::getInstance()->forClassName(\get_class($test)); + + $symbolAnnotations = new \ReflectionProperty($docBlock, 'symbolAnnotations'); + $symbolAnnotations->setAccessible(true); + + // Exclude internal classes; PHPUnit 9.1+ is picky about tests covering, say, a \RuntimeException + $covers = array_filter($covers, function (string $class) { + $reflector = new \ReflectionClass($class); + + return $reflector->isUserDefined(); + }); + + $symbolAnnotations->setValue($docBlock, array_replace($docBlock->symbolAnnotations(), [ + 'covers' => $covers, + ])); } } diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/CoverageListenerForV7.php b/src/Symfony/Bridge/PhpUnit/Legacy/CoverageListenerForV7.php deleted file mode 100644 index a35034c48b..0000000000 --- a/src/Symfony/Bridge/PhpUnit/Legacy/CoverageListenerForV7.php +++ /dev/null @@ -1,41 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\PhpUnit\Legacy; - -use PHPUnit\Framework\Test; -use PHPUnit\Framework\TestListener; -use PHPUnit\Framework\TestListenerDefaultImplementation; - -/** - * CoverageListener adds `@covers ` on each test when possible to - * make the code coverage more accurate. - * - * @author Grégoire Pineau - * - * @internal - */ -class CoverageListenerForV7 implements TestListener -{ - use TestListenerDefaultImplementation; - - private $trait; - - public function __construct(callable $sutFqcnResolver = null, $warningOnSutNotFound = false) - { - $this->trait = new CoverageListenerTrait($sutFqcnResolver, $warningOnSutNotFound); - } - - public function startTest(Test $test): void - { - $this->trait->startTest($test); - } -} diff --git a/src/Symfony/Bridge/PhpUnit/Legacy/CoverageListenerTrait.php b/src/Symfony/Bridge/PhpUnit/Legacy/CoverageListenerTrait.php deleted file mode 100644 index 4ca396ece1..0000000000 --- a/src/Symfony/Bridge/PhpUnit/Legacy/CoverageListenerTrait.php +++ /dev/null @@ -1,160 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\PhpUnit\Legacy; - -use PHPUnit\Framework\TestCase; -use PHPUnit\Framework\Warning; -use PHPUnit\Util\Annotation\Registry; -use PHPUnit\Util\Test; - -/** - * PHP 5.3 compatible trait-like shared implementation. - * - * @author Grégoire Pineau - * - * @internal - */ -class CoverageListenerTrait -{ - private $sutFqcnResolver; - private $warningOnSutNotFound; - private $warnings; - - public function __construct(callable $sutFqcnResolver = null, $warningOnSutNotFound = false) - { - $this->sutFqcnResolver = $sutFqcnResolver; - $this->warningOnSutNotFound = $warningOnSutNotFound; - $this->warnings = []; - } - - public function startTest($test) - { - if (!$test instanceof TestCase) { - return; - } - - $annotations = Test::parseTestMethodAnnotations(\get_class($test), $test->getName(false)); - - $ignoredAnnotations = ['covers', 'coversDefaultClass', 'coversNothing']; - - foreach ($ignoredAnnotations as $annotation) { - if (isset($annotations['class'][$annotation]) || isset($annotations['method'][$annotation])) { - return; - } - } - - $sutFqcn = $this->findSutFqcn($test); - if (!$sutFqcn) { - if ($this->warningOnSutNotFound) { - $message = 'Could not find the tested class.'; - // addWarning does not exist on old PHPUnit version - if (method_exists($test->getTestResultObject(), 'addWarning') && class_exists(Warning::class)) { - $test->getTestResultObject()->addWarning($test, new Warning($message), 0); - } else { - $this->warnings[] = sprintf("%s::%s\n%s", \get_class($test), $test->getName(), $message); - } - } - - return; - } - - $covers = $sutFqcn; - if (!\is_array($sutFqcn)) { - $covers = [$sutFqcn]; - while ($parent = get_parent_class($sutFqcn)) { - $covers[] = $parent; - $sutFqcn = $parent; - } - } - - if (class_exists(Registry::class)) { - $this->addCoversForDocBlockInsideRegistry($test, $covers); - - return; - } - - $this->addCoversForClassToAnnotationCache($test, $covers); - } - - private function addCoversForClassToAnnotationCache($test, $covers) - { - $r = new \ReflectionProperty(Test::class, 'annotationCache'); - $r->setAccessible(true); - - $cache = $r->getValue(); - $cache = array_replace_recursive($cache, [ - \get_class($test) => [ - 'covers' => $covers, - ], - ]); - - $r->setValue(Test::class, $cache); - } - - private function addCoversForDocBlockInsideRegistry($test, $covers) - { - $docBlock = Registry::getInstance()->forClassName(\get_class($test)); - - $symbolAnnotations = new \ReflectionProperty($docBlock, 'symbolAnnotations'); - $symbolAnnotations->setAccessible(true); - - // Exclude internal classes; PHPUnit 9.1+ is picky about tests covering, say, a \RuntimeException - $covers = array_filter($covers, function ($class) { - $reflector = new \ReflectionClass($class); - - return $reflector->isUserDefined(); - }); - - $symbolAnnotations->setValue($docBlock, array_replace($docBlock->symbolAnnotations(), [ - 'covers' => $covers, - ])); - } - - private function findSutFqcn($test) - { - if ($this->sutFqcnResolver) { - $resolver = $this->sutFqcnResolver; - - return $resolver($test); - } - - $class = \get_class($test); - - $sutFqcn = str_replace('\\Tests\\', '\\', $class); - $sutFqcn = preg_replace('{Test$}', '', $sutFqcn); - - return class_exists($sutFqcn) ? $sutFqcn : null; - } - - public function __sleep() - { - throw new \BadMethodCallException('Cannot serialize '.__CLASS__); - } - - public function __wakeup() - { - throw new \BadMethodCallException('Cannot unserialize '.__CLASS__); - } - - public function __destruct() - { - if (!$this->warnings) { - return; - } - - echo "\n"; - - foreach ($this->warnings as $key => $warning) { - echo sprintf("%d) %s\n", ++$key, $warning); - } - } -} diff --git a/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/bootstrap.php b/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/bootstrap.php index 6d78d06f6b..e302fa05ea 100644 --- a/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/bootstrap.php +++ b/src/Symfony/Bridge/PhpUnit/Tests/Fixtures/coverage/tests/bootstrap.php @@ -12,6 +12,4 @@ require __DIR__.'/../src/BarCov.php'; require __DIR__.'/../src/FooCov.php'; -require __DIR__.'/../../../../Legacy/CoverageListenerTrait.php'; -require_once __DIR__.'/../../../../Legacy/CoverageListenerForV7.php'; require __DIR__.'/../../../../CoverageListener.php';