From 928e6e381b282743cb4ca03475b264eb69ee0915 Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Wed, 29 Jan 2025 13:39:26 +0800 Subject: [PATCH] [AnnotationsToAttributes] Fix @covers qualified::method conversion --- .../Fixture/covers_class.php.inc | 2 ++ .../Fixture/covers_method.php.inc | 12 +++++++++++ ...rsAnnotationWithValueToAttributeRector.php | 20 +++++++++++++++---- .../AssertFuncCallToPHPUnitAssertRector.php | 1 + .../Fixture/some_test.php.inc | 3 +-- 5 files changed, 32 insertions(+), 6 deletions(-) diff --git a/rules-tests/AnnotationsToAttributes/Rector/Class_/CoversAnnotationWithValueToAttributeRector/Fixture/covers_class.php.inc b/rules-tests/AnnotationsToAttributes/Rector/Class_/CoversAnnotationWithValueToAttributeRector/Fixture/covers_class.php.inc index 9017797c..1c60fc6f 100644 --- a/rules-tests/AnnotationsToAttributes/Rector/Class_/CoversAnnotationWithValueToAttributeRector/Fixture/covers_class.php.inc +++ b/rules-tests/AnnotationsToAttributes/Rector/Class_/CoversAnnotationWithValueToAttributeRector/Fixture/covers_class.php.inc @@ -6,6 +6,7 @@ use PHPUnit\Framework\TestCase; /** * @covers \Rector\PHPUnit\Tests\AnnotationsToAttributes\Rector\Class_\CoversAnnotationWithValueToAttributeRector\Source\ExistingClass + * @covers \Rector\PHPUnit\Tests\AnnotationsToAttributes\Rector\Class_\CoversAnnotationWithValueToAttributeRector\Source\AnotherExistingClass::someMethod */ final class CoversClass extends TestCase { @@ -23,6 +24,7 @@ namespace Rector\PHPUnit\Tests\AnnotationsToAttributes\Rector\Class_\CoversAnnot use PHPUnit\Framework\TestCase; #[\PHPUnit\Framework\Attributes\CoversClass(\Rector\PHPUnit\Tests\AnnotationsToAttributes\Rector\Class_\CoversAnnotationWithValueToAttributeRector\Source\ExistingClass::class)] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Rector\PHPUnit\Tests\AnnotationsToAttributes\Rector\Class_\CoversAnnotationWithValueToAttributeRector\Source\AnotherExistingClass::class, 'someMethod')] final class CoversClass extends TestCase { public function test() diff --git a/rules-tests/AnnotationsToAttributes/Rector/Class_/CoversAnnotationWithValueToAttributeRector/Fixture/covers_method.php.inc b/rules-tests/AnnotationsToAttributes/Rector/Class_/CoversAnnotationWithValueToAttributeRector/Fixture/covers_method.php.inc index 9da469a7..d8bc445b 100644 --- a/rules-tests/AnnotationsToAttributes/Rector/Class_/CoversAnnotationWithValueToAttributeRector/Fixture/covers_method.php.inc +++ b/rules-tests/AnnotationsToAttributes/Rector/Class_/CoversAnnotationWithValueToAttributeRector/Fixture/covers_method.php.inc @@ -12,6 +12,13 @@ final class CoversMethod extends TestCase public function test() { } + + /** + * @covers \Rector\PHPUnit\Tests\AnnotationsToAttributes\Rector\Class_\CoversAnnotationWithValueToAttributeRector\Source\AnotherExistingClass::someMethod + */ + public function test_foo() + { + } } ?> @@ -23,11 +30,16 @@ namespace Rector\PHPUnit\Tests\AnnotationsToAttributes\Rector\Class_\CoversAnnot use PHPUnit\Framework\TestCase; #[\PHPUnit\Framework\Attributes\CoversClass(\Rector\PHPUnit\Tests\AnnotationsToAttributes\Rector\Class_\CoversAnnotationWithValueToAttributeRector\Source\ExistingClass::class)] +#[\PHPUnit\Framework\Attributes\CoversMethod(\Rector\PHPUnit\Tests\AnnotationsToAttributes\Rector\Class_\CoversAnnotationWithValueToAttributeRector\Source\AnotherExistingClass::class, 'someMethod')] final class CoversMethod extends TestCase { public function test() { } + + public function test_foo() + { + } } ?> diff --git a/rules/AnnotationsToAttributes/Rector/Class_/CoversAnnotationWithValueToAttributeRector.php b/rules/AnnotationsToAttributes/Rector/Class_/CoversAnnotationWithValueToAttributeRector.php index e8fae20a..559e90d1 100644 --- a/rules/AnnotationsToAttributes/Rector/Class_/CoversAnnotationWithValueToAttributeRector.php +++ b/rules/AnnotationsToAttributes/Rector/Class_/CoversAnnotationWithValueToAttributeRector.php @@ -38,6 +38,11 @@ final class CoversAnnotationWithValueToAttributeRector extends AbstractRector im */ private const COVERTS_CLASS_ATTRIBUTE = 'PHPUnit\Framework\Attributes\CoversClass'; + /** + * @var string + */ + private const COVERS_METHOD_ATTRIBUTE = 'PHPUnit\Framework\Attributes\CoversMethod'; + public function __construct( private readonly PhpDocTagRemover $phpDocTagRemover, private readonly PhpAttributeGroupFactory $phpAttributeGroupFactory, @@ -139,13 +144,16 @@ private function createAttributeGroup(string $annotationValue): AttributeGroup { if (str_starts_with($annotationValue, '::')) { $attributeClass = self::COVERS_FUNCTION_ATTRIBUTE; - $attributeValue = trim($annotationValue, ':()'); + $attributeValue = [trim($annotationValue, ':()')]; + } elseif (str_contains($annotationValue, '::')) { + $attributeClass = self::COVERS_METHOD_ATTRIBUTE; + $attributeValue = [$this->getClass($annotationValue) . '::class', $this->getMethod($annotationValue)]; } else { $attributeClass = self::COVERTS_CLASS_ATTRIBUTE; - $attributeValue = trim($annotationValue) . '::class'; + $attributeValue = [trim($annotationValue) . '::class']; } - return $this->phpAttributeGroupFactory->createFromClassWithItems($attributeClass, [$attributeValue]); + return $this->phpAttributeGroupFactory->createFromClassWithItems($attributeClass, $attributeValue); } /** @@ -235,7 +243,6 @@ private function resolveMethodAttributes(ClassMethod $classMethod, bool $hasCove $covers = $desiredTagValueNode->value->value; if (str_starts_with($covers, '\\')) { - $covers = $this->getClass($covers); $attributeGroups[$covers] = $this->createAttributeGroup($covers); } elseif (! $hasCoversDefault && str_starts_with($covers, '::')) { $attributeGroups[$covers] = $this->createAttributeGroup($covers); @@ -271,4 +278,9 @@ private function getClass(string $classWithMethod): string { return Strings::replace($classWithMethod, '/::.*$/'); } + + private function getMethod(string $classWithMethod): string + { + return Strings::replace($classWithMethod, '/^.*::/'); + } } diff --git a/rules/CodeQuality/Rector/FuncCall/AssertFuncCallToPHPUnitAssertRector.php b/rules/CodeQuality/Rector/FuncCall/AssertFuncCallToPHPUnitAssertRector.php index 50508fa6..3389b304 100644 --- a/rules/CodeQuality/Rector/FuncCall/AssertFuncCallToPHPUnitAssertRector.php +++ b/rules/CodeQuality/Rector/FuncCall/AssertFuncCallToPHPUnitAssertRector.php @@ -149,6 +149,7 @@ private function isTestFilePath(FuncCall $funcCall): bool if (str_ends_with($className, 'Test')) { return true; } + return str_ends_with($className, 'TestCase'); } diff --git a/tests/Issues/DoubleAnnotation/Fixture/some_test.php.inc b/tests/Issues/DoubleAnnotation/Fixture/some_test.php.inc index 9b4bc8de..75e6ae0c 100644 --- a/tests/Issues/DoubleAnnotation/Fixture/some_test.php.inc +++ b/tests/Issues/DoubleAnnotation/Fixture/some_test.php.inc @@ -29,7 +29,7 @@ namespace Rector\PHPUnit\Tests\Issues\DoubleAnnotation\Fixture; use PHPUnit\Framework\TestCase; use Rector\PHPUnit\Tests\CodeQuality\Rector\MethodCall\UseSpecificWillMethodRector\Fixture\SomeClass; -#[\PHPUnit\Framework\Attributes\CoversClass(\SomeClass::class)] +#[\PHPUnit\Framework\Attributes\CoversMethod(\SomeClass::class, 'method')] final class SomeTest extends TestCase { #[\PHPUnit\Framework\Attributes\DataProvider('generatorInput')] @@ -42,4 +42,3 @@ final class SomeTest extends TestCase { } } -