From 280f90bf80d5be3bcf60b72256f2b0f2dfd48a17 Mon Sep 17 00:00:00 2001 From: Valentin Udaltsov Date: Thu, 8 Feb 2024 12:49:28 +0300 Subject: [PATCH] Improved ReflectorCompatibilityTest --- composer.json | 9 ++-- src/FileResource.php | 2 +- tests/unit/ReflectorCompatibilityTest.php | 55 ++++++++++++++++------- 3 files changed, 45 insertions(+), 21 deletions(-) diff --git a/composer.json b/composer.json index 24077b2..57d7edf 100644 --- a/composer.json +++ b/composer.json @@ -19,14 +19,15 @@ "typhoon/type-stringifier": "^0.3@dev" }, "require-dev": { - "ergebnis/composer-normalize": "^2.41.1", - "friendsofphp/php-cs-fixer": "^3.47.1", + "ergebnis/composer-normalize": "^2.42.0", + "friendsofphp/php-cs-fixer": "^3.49.0", "infection/infection": "^0.27.9", "jetbrains/phpstorm-stubs": "^2023.3", - "phpunit/phpunit": "^10.5.8", + "mockery/mockery": "^1.6.7", + "phpunit/phpunit": "^10.5.10", "phpyh/coding-standard": "^2.6.0", "psalm/plugin-phpunit": "^0.18.4", - "symfony/var-dumper": "^6.4.2 || ^7", + "symfony/var-dumper": "^6.4.2 || ^7.0.3", "typhoon/opcache": "^0.2@dev", "typhoon/psalm-plugin": "^0.1.0", "vimeo/psalm": "^5@dev" diff --git a/src/FileResource.php b/src/FileResource.php index 80ca222..66f7a40 100644 --- a/src/FileResource.php +++ b/src/FileResource.php @@ -43,7 +43,7 @@ public function contents(): string $contents = file_get_contents($this->file); if ($contents === false) { - throw new \RuntimeException(); + throw new \RuntimeException(sprintf('Failed to read file %s.', $this->file)); } return $this->contents = $contents; diff --git a/tests/unit/ReflectorCompatibilityTest.php b/tests/unit/ReflectorCompatibilityTest.php index 1bcb2c6..ddea17c 100644 --- a/tests/unit/ReflectorCompatibilityTest.php +++ b/tests/unit/ReflectorCompatibilityTest.php @@ -4,18 +4,28 @@ namespace Typhoon\Reflection; +use Mockery\Loader\RequireLoader; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\RequiresPhp; use PHPUnit\Framework\TestCase; use Typhoon\Reflection\NameContext\AnonymousClassName; +#[CoversClass(AttributeReflection::class)] #[CoversClass(ClassReflection::class)] +#[CoversClass(MethodReflection::class)] +#[CoversClass(ParameterReflection::class)] +#[CoversClass(PropertyReflection::class)] final class ReflectorCompatibilityTest extends TestCase { private const CLASSES = __DIR__ . '/ReflectorCompatibility/classes.php'; private const READONLY_CLASSES = __DIR__ . '/ReflectorCompatibility/readonly_classes.php'; + public static function setUpBeforeClass(): void + { + \Mockery::setLoader(new RequireLoader(__DIR__ . '/../../var/mockery')); + } + /** * @return \Generator> */ @@ -150,7 +160,9 @@ private function assertClassEquals(\ReflectionClass $native, ClassReflection $ty self::assertSame($native->isCloneable(), $typhoon->isCloneable(), 'class.isCloneable()'); self::assertSame($native->isEnum(), $typhoon->isEnum(), 'class.isEnum()'); self::assertSame($native->isFinal(), $typhoon->isFinal(), 'class.isFinal()'); - // TODO isInstance + if ($this->canCreateMockObject($native)) { + self::assertSame($native->isInstance($this->createMockObject($native)), $typhoon->isInstance($this->createMockObject($native)), 'class.isInstance()'); + } self::assertSame($native->isInstantiable(), $typhoon->isInstantiable(), 'class.isInstantiable()'); self::assertSame($native->isInterface(), $typhoon->isInterface(), 'class.isInterface()'); self::assertSame($native->isInternal(), $typhoon->isInternal(), 'class.isInternal()'); @@ -222,16 +234,14 @@ private function assertPropertyEquals(\ReflectionProperty $native, PropertyRefle private function assertMethodEquals(\ReflectionMethod $native, MethodReflection $typhoon, string $messagePrefix): void { - $declaringClass = $native->getDeclaringClass(); - self::assertSame($native->class, $typhoon->class, $messagePrefix . '.class'); self::assertSame($native->name, $typhoon->name, $messagePrefix . '.name'); // TODO: self::assertSame($native->__toString(), $typhoon->__toString(), $messagePrefix . '.__toString()'); self::assertAttributesEqual($native->getAttributes(), $typhoon->getAttributes(), $messagePrefix . 'getAttributes()'); if ($native->isStatic()) { $this->assertMethodClosureEquals($native->getClosure(), $typhoon->getClosure(), $messagePrefix . '.getClosure()'); - } elseif (!($declaringClass->isTrait() || $declaringClass instanceof \ReflectionEnum && $declaringClass->getCases() === [])) { - $object = $this->createMockObject($native->class); + } elseif ($this->canCreateMockObject($native->getDeclaringClass())) { + $object = $this->createMockObject($native->getDeclaringClass()); $this->assertMethodClosureEquals($native->getClosure($object), $typhoon->getClosure($object), $messagePrefix . '.getClosure($object)'); } self::assertSame($native->getClosureCalledClass(), $typhoon->getClosureCalledClass(), $messagePrefix . '.getClosureCalledClass()'); @@ -310,7 +320,7 @@ private function assertParameterEquals(\ReflectionParameter $native, \Reflection $native->isDefaultValueAvailable() && self::assertSame($native->getDefaultValueConstantName(), $typhoon->getDefaultValueConstantName(), $messagePrefix . '.getDefaultValueConstantName()'); self::assertSame($native->getName(), $typhoon->getName(), $messagePrefix . '.getName()'); self::assertSame($native->getPosition(), $typhoon->getPosition(), $messagePrefix . '.getPosition()'); - // TODO getType() + self::assertEquals($native->getType(), $typhoon->getType(), $messagePrefix . '.getType()'); self::assertSame($native->hasType(), $typhoon->hasType(), $messagePrefix . '.hasType()'); self::assertSame($native->isArray(), $typhoon->isArray(), $messagePrefix . '.isArray()'); self::assertSame($native->isCallable(), $typhoon->isCallable(), $messagePrefix . '.isCallable()'); @@ -364,33 +374,46 @@ private function assertSameNames(array $nativeReflections, array $typhoonReflect self::assertSame(array_column($nativeReflections, 'name'), array_column($typhoonReflections, 'name'), $message); } + private function canCreateMockObject(\ReflectionClass $class): bool + { + if ($class->isTrait()) { + return false; + } + + if ($class->isEnum()) { + /** @psalm-suppress MixedMethodCall */ + return $class->name::cases() !== []; + } + + return true; + } + /** * @template T of object - * @param class-string $class + * @param \ReflectionClass $class * @return T */ - private function createMockObject(string $class): object + private function createMockObject(\ReflectionClass $class): object { - $reflection = new \ReflectionClass($class); - - if ($reflection->isAbstract() || $reflection->isInterface()) { - return $this->createMock($class); + if ($class->isAbstract() || $class->isInterface()) { + /** @var T */ + return \Mockery::mock($class->name); } - if (enum_exists($class)) { + if ($class->isEnum()) { /** * @var list * @psalm-suppress MixedMethodCall */ - $cases = $class::cases(); + $cases = $class->name::cases(); if ($cases === []) { - throw new \LogicException(sprintf('Enum %s has no cases.', $class)); + throw new \LogicException(sprintf('Enum %s has no cases.', $class->name)); } return $cases[0]; } - return $reflection->newInstanceWithoutConstructor(); + return $class->newInstanceWithoutConstructor(); } }