Skip to content

Commit

Permalink
Improved ReflectorCompatibilityTest
Browse files Browse the repository at this point in the history
  • Loading branch information
vudaltsov committed Feb 8, 2024
1 parent 0cc8c79 commit 280f90b
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 21 deletions.
9 changes: 5 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion src/FileResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
55 changes: 39 additions & 16 deletions tests/unit/ReflectorCompatibilityTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, list<ClassLocator>>
*/
Expand Down Expand Up @@ -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()');
Expand Down Expand Up @@ -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()');
Expand Down Expand Up @@ -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()');
Expand Down Expand Up @@ -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<T> $class
* @param \ReflectionClass<T> $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<T>
* @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();
}
}

0 comments on commit 280f90b

Please sign in to comment.