Skip to content

Commit

Permalink
fix tests
Browse files Browse the repository at this point in the history
  • Loading branch information
rubenvanassche committed Aug 13, 2024
1 parent 9d7edfa commit 6fc4ed6
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 80 deletions.
51 changes: 15 additions & 36 deletions src/Support/Annotations/CollectionAnnotationReader.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,72 +14,56 @@

class CollectionAnnotationReader
{
/** @var array<class-string, CollectionAnnotation|null> */
protected static array $cache = [];

protected Context $context;

public function __construct(
protected readonly ContextResolver $contextResolver,
protected readonly TypeResolver $typeResolver,
) {
}

/** @var array<class-string, CollectionAnnotation|null> */
protected static array $cache = [];

protected Context $context;

/**
* @param class-string $className
*/
public function getForClass(string $className): ?CollectionAnnotation
{
// Check the cache first
if (array_key_exists($className, self::$cache)) {
return self::$cache[$className];
}

// Create ReflectionClass from class string
$class = $this->getReflectionClass($className);
$class = new ReflectionClass($className);

if(empty($class->getDocComment())) {
return self::$cache[$className] = null;
}

// Determine if the class is a collection
if (! $this->isCollection($class)) {
if (! $this->isIterable($class)) {
return self::$cache[$className] = null;
}

// Get the collection return type
$type = $this->getCollectionReturnType($class);

if ($type === null || $type['valueType'] === null) {
return self::$cache[$className] = null;
}

$isData = is_subclass_of($type['valueType'], Data::class);

$annotation = new CollectionAnnotation(
return self::$cache[$className] = new CollectionAnnotation(
type: $type['valueType'],
isData: $isData,
isData: is_subclass_of($type['valueType'], Data::class),
keyType: $type['keyType'] ?? 'array-key',
);

// Cache the result
self::$cache[$className] = $annotation;

return $annotation;
}

public static function clearCache(): void
{
self::$cache = [];
}

/**
* @param class-string $className
*/
protected function getReflectionClass(string $className): ReflectionClass
{
return new ReflectionClass($className);
}

protected function isCollection(ReflectionClass $class): bool
protected function isIterable(ReflectionClass $class): bool
{
// Check if the class implements common collection interfaces
$collectionInterfaces = [
Iterator::class,
IteratorAggregate::class,
Expand All @@ -99,27 +83,23 @@ protected function isCollection(ReflectionClass $class): bool
*/
protected function getCollectionReturnType(ReflectionClass $class): ?array
{
// Initialize TypeResolver and DocBlockFactory
$docBlockFactory = DocBlockFactory::createInstance();

$this->context = $this->contextResolver->execute($class);

// Get the PHPDoc comment of the class
$docComment = $class->getDocComment();

if ($docComment === false) {
return null;
}

// Create the DocBlock instance
$docBlock = $docBlockFactory->create($docComment, $this->context);

// Initialize variables
$templateTypes = [];
$keyType = null;
$valueType = null;

foreach ($docBlock->getTags() as $tag) {

if (! $tag instanceof Generic) {
continue;
}
Expand All @@ -138,7 +118,6 @@ protected function getCollectionReturnType(ReflectionClass $class): ?array
$description = $tag->getDescription();

if (preg_match('/<\s*([^,\s]+)?\s*(?:,\s*([^>\s]+))?\s*>/', $description, $matches)) {

if (count($matches) === 3) {
$keyType = $templateTypes[$matches[1]] ?? $this->resolve($matches[1]);
$valueType = $templateTypes[$matches[2]] ?? $this->resolve($matches[2]);
Expand Down
14 changes: 3 additions & 11 deletions tests/Support/Annotations/CollectionAnnotationReaderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,28 +108,20 @@ function (string $className, ?CollectionAnnotation $expected) {

it('can caches the result', function (string $className) {

// Create a partial mock
$collectionAnnotationReader = Mockery::spy(CollectionAnnotationReader::class, [
app(ContextResolver::class),
app(TypeResolver::class),
])->makePartial();

// Call the getForClass method with a test class
$collectionAnnotation = $collectionAnnotationReader->getForClass($className);

// Call the getForClass method again to test caching
$cachedCollectionAnnotation = $collectionAnnotationReader->getForClass($className);

// Assert the cache is used and the same annotation is returned
expect($cachedCollectionAnnotation)->toBe($collectionAnnotation);

// Check if getReflectionClass was called only once
$collectionAnnotationReader->shouldHaveReceived('getReflectionClass')->once();

})->with([
[CollectionWhoImplementsNothing::class], // first return
[CollectionWithoutDocBlock::class], // second return
[DataCollectionWithTemplate::class], // third return
[CollectionWhoImplementsNothing::class],
[CollectionWithoutDocBlock::class],
[DataCollectionWithTemplate::class],
]);

/**
Expand Down
66 changes: 33 additions & 33 deletions tests/Support/Annotations/DataIterableAnnotationReaderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,31 +92,31 @@ function (string $property, ?DataIterableAnnotation $expected) {
$annotations = app(DataIterableAnnotationReader::class)->getForClass(new ReflectionClass(CollectionDataAnnotationsData::class));

expect($annotations)->toEqualCanonicalizing([
new DataIterableAnnotation(SimpleData::class, isData: true, property: 'propertyN'),
new DataIterableAnnotation(SimpleData::class, isData: true, property: 'propertyO'),
new DataIterableAnnotation(SimpleData::class, isData: true, property: 'propertyP'),
new DataIterableAnnotation(SimpleData::class, isData: true, property: 'propertyQ'),
new DataIterableAnnotation(SimpleData::class, isData: true, property: 'propertyR'),
new DataIterableAnnotation(SimpleData::class, isData: true, property: 'propertyS'),
new DataIterableAnnotation(SimpleData::class, isData: true, property: 'propertyT'),
'propertyN' => new DataIterableAnnotation(SimpleData::class, isData: true, property: 'propertyN'),
'propertyO' => new DataIterableAnnotation(SimpleData::class, isData: true, property: 'propertyO'),
'propertyP' => new DataIterableAnnotation(SimpleData::class, isData: true, property: 'propertyP'),
'propertyQ' => new DataIterableAnnotation(SimpleData::class, isData: true, property: 'propertyQ'),
'propertyR' => new DataIterableAnnotation(SimpleData::class, isData: true, property: 'propertyR'),
'propertyS' => new DataIterableAnnotation(SimpleData::class, isData: true, property: 'propertyS'),
'propertyT' => new DataIterableAnnotation(SimpleData::class, isData: true, property: 'propertyT'),
]);
});

it('can get data class for a data collection by method annotation', function () {
$annotations = app(DataIterableAnnotationReader::class)->getForMethod(new ReflectionMethod(CollectionDataAnnotationsData::class, 'method'));

expect($annotations)->toEqualCanonicalizing([
new DataIterableAnnotation(SimpleData::class, isData: true, property: 'paramA'),
new DataIterableAnnotation(SimpleData::class, isData: true, property: 'paramB'),
new DataIterableAnnotation(SimpleData::class, isData: true, property: 'paramC'),
new DataIterableAnnotation(SimpleData::class, isData: true, property: 'paramD'),
new DataIterableAnnotation(SimpleData::class, isData: true, property: 'paramE'),
new DataIterableAnnotation(SimpleData::class, isData: true, property: 'paramF'),
new DataIterableAnnotation(SimpleData::class, isData: true, property: 'paramG'),
new DataIterableAnnotation(SimpleData::class, isData: true, property: 'paramH'),
new DataIterableAnnotation(SimpleData::class, isData: true, keyType: 'int', property: 'paramJ'),
new DataIterableAnnotation(SimpleData::class, isData: true, keyType: 'int', property: 'paramI'),
new DataIterableAnnotation(SimpleData::class, isData: true, property: 'paramK'),
'paramA' => new DataIterableAnnotation(SimpleData::class, isData: true, property: 'paramA'),
'paramB' => new DataIterableAnnotation(SimpleData::class, isData: true, property: 'paramB'),
'paramC' => new DataIterableAnnotation(SimpleData::class, isData: true, property: 'paramC'),
'paramD' => new DataIterableAnnotation(SimpleData::class, isData: true, property: 'paramD'),
'paramE' => new DataIterableAnnotation(SimpleData::class, isData: true, property: 'paramE'),
'paramF' => new DataIterableAnnotation(SimpleData::class, isData: true, property: 'paramF'),
'paramG' => new DataIterableAnnotation(SimpleData::class, isData: true, property: 'paramG'),
'paramH' => new DataIterableAnnotation(SimpleData::class, isData: true, property: 'paramH'),
'paramJ' => new DataIterableAnnotation(SimpleData::class, isData: true, keyType: 'int', property: 'paramJ'),
'paramI' => new DataIterableAnnotation(SimpleData::class, isData: true, keyType: 'int', property: 'paramI'),
'paramK' => new DataIterableAnnotation(SimpleData::class, isData: true, property: 'paramK'),
]);
});

Expand Down Expand Up @@ -203,28 +203,28 @@ function (string $property, ?DataIterableAnnotation $expected) {
$annotations = app(DataIterableAnnotationReader::class)->getForClass(new ReflectionClass(CollectionNonDataAnnotationsData::class));

expect($annotations)->toEqualCanonicalizing([
new DataIterableAnnotation(DummyBackedEnum::class, isData: false, property: 'propertyM'),
new DataIterableAnnotation(DummyBackedEnum::class, isData: false, property: 'propertyN'),
new DataIterableAnnotation(DummyBackedEnum::class, isData: false, property: 'propertyO'),
new DataIterableAnnotation(DummyBackedEnum::class, isData: false, property: 'propertyQ'),
'propertyM' => new DataIterableAnnotation(DummyBackedEnum::class, isData: false, property: 'propertyM'),
'propertyN' => new DataIterableAnnotation(DummyBackedEnum::class, isData: false, property: 'propertyN'),
'propertyO' => new DataIterableAnnotation(DummyBackedEnum::class, isData: false, property: 'propertyO'),
'propertyQ' => new DataIterableAnnotation(DummyBackedEnum::class, isData: false, property: 'propertyQ'),
]);
});

it('can get iterable class for a data by method annotation', function () {
$annotations = app(DataIterableAnnotationReader::class)->getForMethod(new ReflectionMethod(CollectionNonDataAnnotationsData::class, 'method'));

expect($annotations)->toEqualCanonicalizing([
new DataIterableAnnotation(DummyBackedEnum::class, isData: false, property: 'paramA'),
new DataIterableAnnotation(DummyBackedEnum::class, isData: false, property: 'paramB'),
new DataIterableAnnotation(DummyBackedEnum::class, isData: false, property: 'paramC'),
new DataIterableAnnotation(DummyBackedEnum::class, isData: false, property: 'paramD'),
new DataIterableAnnotation(DummyBackedEnum::class, isData: false, property: 'paramE'),
new DataIterableAnnotation(DummyBackedEnum::class, isData: false, property: 'paramF'),
new DataIterableAnnotation(DummyBackedEnum::class, isData: false, property: 'paramG'),
new DataIterableAnnotation(DummyBackedEnum::class, isData: false, property: 'paramH'),
new DataIterableAnnotation(DummyBackedEnum::class, isData: false, keyType: 'int', property: 'paramJ'),
new DataIterableAnnotation(DummyBackedEnum::class, isData: false, keyType: 'int', property: 'paramI'),
new DataIterableAnnotation(DummyBackedEnum::class, isData: false, property: 'paramK'),
'paramA' => new DataIterableAnnotation(DummyBackedEnum::class, isData: false, property: 'paramA'),
'paramB' => new DataIterableAnnotation(DummyBackedEnum::class, isData: false, property: 'paramB'),
'paramC' => new DataIterableAnnotation(DummyBackedEnum::class, isData: false, property: 'paramC'),
'paramD' => new DataIterableAnnotation(DummyBackedEnum::class, isData: false, property: 'paramD'),
'paramE' => new DataIterableAnnotation(DummyBackedEnum::class, isData: false, property: 'paramE'),
'paramF' => new DataIterableAnnotation(DummyBackedEnum::class, isData: false, property: 'paramF'),
'paramG' => new DataIterableAnnotation(DummyBackedEnum::class, isData: false, property: 'paramG'),
'paramH' => new DataIterableAnnotation(DummyBackedEnum::class, isData: false, property: 'paramH'),
'paramJ' => new DataIterableAnnotation(DummyBackedEnum::class, isData: false, keyType: 'int', property: 'paramJ'),
'paramI' => new DataIterableAnnotation(DummyBackedEnum::class, isData: false, keyType: 'int', property: 'paramI'),
'paramK' => new DataIterableAnnotation(DummyBackedEnum::class, isData: false, property: 'paramK'),
]);
});

Expand Down

0 comments on commit 6fc4ed6

Please sign in to comment.