1<?php 2 3declare(strict_types=1); 4 5namespace Metadata\Tests; 6 7use Metadata\ClassMetadata; 8use Metadata\MergeableClassMetadata; 9use Metadata\MetadataFactory; 10use Metadata\PropertyMetadata; 11use PHPUnit\Framework\TestCase; 12 13class MetadataFactoryTest extends TestCase 14{ 15 public function testGetMetadataForClass() 16 { 17 $driver = $this->createMock('Metadata\Driver\DriverInterface'); 18 19 $driver 20 ->expects($this->at(0)) 21 ->method('loadMetadataForClass') 22 ->with($this->equalTo(new \ReflectionClass('Metadata\Tests\Fixtures\TestObject'))) 23 ->will($this->returnCallback(function ($class) { 24 return new ClassMetadata($class->getName()); 25 })) 26 ; 27 $driver 28 ->expects($this->at(1)) 29 ->method('loadMetadataForClass') 30 ->with($this->equalTo(new \ReflectionClass('Metadata\Tests\Fixtures\TestParent'))) 31 ->will($this->returnCallback(function ($class) { 32 return new ClassMetadata($class->getName()); 33 })) 34 ; 35 36 $factory = new MetadataFactory($driver); 37 $metadata = $factory->getMetadataForClass('Metadata\Tests\Fixtures\TestParent'); 38 39 $this->assertInstanceOf('Metadata\ClassHierarchyMetadata', $metadata); 40 $this->assertEquals(2, count($metadata->classMetadata)); 41 } 42 43 public function testGetMetadataForClassWhenMergeable() 44 { 45 $driver = $this->createMock('Metadata\Driver\DriverInterface'); 46 47 $driver 48 ->expects($this->at(0)) 49 ->method('loadMetadataForClass') 50 ->with($this->equalTo(new \ReflectionClass('Metadata\Tests\Fixtures\TestObject'))) 51 ->will($this->returnCallback(function ($class) { 52 return new MergeableClassMetadata($class->getName()); 53 })) 54 ; 55 $driver 56 ->expects($this->at(1)) 57 ->method('loadMetadataForClass') 58 ->with($this->equalTo(new \ReflectionClass('Metadata\Tests\Fixtures\TestParent'))) 59 ->will($this->returnCallback(function ($class) { 60 return new MergeableClassMetadata($class->getName()); 61 })) 62 ; 63 64 $factory = new MetadataFactory($driver); 65 $metadata = $factory->getMetadataForClass('Metadata\Tests\Fixtures\TestParent'); 66 67 $this->assertInstanceOf('Metadata\MergeableClassMetadata', $metadata); 68 $this->assertEquals('Metadata\Tests\Fixtures\TestParent', $metadata->name); 69 } 70 71 public function testGetMetadataWithComplexHierarchy() 72 { 73 $driver = $this->createMock('Metadata\Driver\DriverInterface'); 74 75 $driver 76 ->expects($this->any()) 77 ->method('loadMetadataForClass') 78 ->will($this->returnCallback(function ($class) { 79 $metadata = new MergeableClassMetadata($class->name); 80 81 switch ($class->name) { 82 case 'Metadata\Tests\Fixtures\ComplexHierarchy\BaseClass': 83 $metadata->propertyMetadata['foo'] = new PropertyMetadata($class->name, 'foo'); 84 break; 85 86 case 'Metadata\Tests\Fixtures\ComplexHierarchy\SubClassA': 87 $metadata->propertyMetadata['bar'] = new PropertyMetadata($class->name, 'bar'); 88 break; 89 90 case 'Metadata\Tests\Fixtures\ComplexHierarchy\SubClassB': 91 $metadata->propertyMetadata['baz'] = new PropertyMetadata($class->name, 'baz'); 92 break; 93 94 default: 95 throw new \RuntimeException(sprintf('Unsupported class "%s".', $class->name)); 96 } 97 98 return $metadata; 99 })) 100 ; 101 102 $factory = new MetadataFactory($driver); 103 104 $subClassA = $factory->getMetadataForClass('Metadata\Tests\Fixtures\ComplexHierarchy\SubClassA'); 105 $this->assertInstanceOf('Metadata\MergeableClassMetadata', $subClassA); 106 $this->assertEquals(['foo', 'bar'], array_keys($subClassA->propertyMetadata)); 107 108 $subClassB = $factory->getMetadataForClass('Metadata\Tests\Fixtures\ComplexHierarchy\SubClassB'); 109 $this->assertInstanceOf('Metadata\MergeableClassMetadata', $subClassB); 110 $this->assertEquals(['foo', 'baz'], array_keys($subClassB->propertyMetadata)); 111 } 112 113 public function testGetMetadataWithCache() 114 { 115 $driver = $this->createMock('Metadata\Driver\DriverInterface'); 116 $driver 117 ->expects($this->once()) 118 ->method('loadMetadataForClass') 119 ->will($this->returnValue($metadata = new ClassMetadata('Metadata\Tests\Fixtures\TestObject'))) 120 ; 121 122 $factory = new MetadataFactory($driver); 123 124 $cache = $this->createMock('Metadata\Cache\CacheInterface'); 125 $cache 126 ->expects($this->once()) 127 ->method('load') 128 ->with($this->equalTo('Metadata\Tests\Fixtures\TestObject')) 129 ->will($this->returnValue(null)) 130 ; 131 $cache 132 ->expects($this->once()) 133 ->method('put') 134 ->with($this->equalTo($metadata)) 135 ; 136 $factory->setCache($cache); 137 138 $factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject'); 139 $factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject'); 140 $this->assertSame($metadata, reset($factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject')->classMetadata)); 141 } 142 143 public function testGetMetadataReturnsNullIfNoMetadataIsFound() 144 { 145 $driver = $this->createMock('Metadata\Driver\DriverInterface'); 146 $driver 147 ->expects($this->once()) 148 ->method('loadMetadataForClass') 149 ->will($this->returnValue(null)) 150 ; 151 152 $factory = new MetadataFactory($driver); 153 154 $this->assertNull($factory->getMetadataForClass('stdClass')); 155 } 156 157 public function testGetMetadataWithInterfaces() 158 { 159 $driver = $this->createMock('Metadata\Driver\DriverInterface'); 160 161 $driver 162 ->expects($this->at(3)) 163 ->method('loadMetadataForClass') 164 ->with($this->equalTo(new \ReflectionClass('Metadata\Tests\Fixtures\ComplexHierarchy\SubClassA'))) 165 ; 166 $driver 167 ->expects($this->at(2)) 168 ->method('loadMetadataForClass') 169 ->with($this->equalTo(new \ReflectionClass('Metadata\Tests\Fixtures\ComplexHierarchy\InterfaceB'))) 170 ; 171 $driver 172 ->expects($this->at(1)) 173 ->method('loadMetadataForClass') 174 ->with($this->equalTo(new \ReflectionClass('Metadata\Tests\Fixtures\ComplexHierarchy\BaseClass'))) 175 ; 176 $driver 177 ->expects($this->at(0)) 178 ->method('loadMetadataForClass') 179 ->with($this->equalTo(new \ReflectionClass('Metadata\Tests\Fixtures\ComplexHierarchy\InterfaceA'))) 180 ; 181 182 $factory = new MetadataFactory($driver); 183 $factory->setIncludeInterfaces(true); 184 185 $factory->getMetadataForClass('Metadata\Tests\Fixtures\ComplexHierarchy\SubClassA'); 186 } 187 188 public function testGetAllClassNames() 189 { 190 $driver = $this->createMock('Metadata\Driver\AdvancedDriverInterface'); 191 $driver 192 ->expects($this->once()) 193 ->method('getAllClassNames') 194 ->will($this->returnValue([])); 195 196 $factory = new MetadataFactory($driver); 197 $this->assertSame([], $factory->getAllClassNames()); 198 } 199 200 public function testGetAllClassNamesThrowsException() 201 { 202 $this->expectException('RuntimeException'); 203 $factory = new MetadataFactory($this->createMock('Metadata\Driver\DriverInterface')); 204 $factory->getAllClassNames(); 205 } 206 207 public function testNotFoundMetadataIsCached() 208 { 209 $driver = $this->createMock('Metadata\Driver\DriverInterface'); 210 $driver 211 ->expects($this->once()) // This is the important part of this test 212 ->method('loadMetadataForClass') 213 ->will($this->returnValue(null)) 214 ; 215 216 $cachedMetadata = null; 217 $cache = $this->createMock('Metadata\Cache\CacheInterface'); 218 $cache 219 ->expects($this->any()) 220 ->method('load') 221 ->with($this->equalTo('Metadata\Tests\Fixtures\TestObject')) 222 ->will($this->returnCallback(function () use (&$cachedMetadata) { 223 return $cachedMetadata; 224 })) 225 ; 226 $cache 227 ->expects($this->once()) 228 ->method('put') 229 ->will($this->returnCallback(function ($metadata) use (&$cachedMetadata) { 230 $cachedMetadata = $metadata; 231 })) 232 ; 233 234 $factory = new MetadataFactory($driver); 235 $factory->setCache($cache); 236 $factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject'); 237 $factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject'); 238 $this->assertNull($factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject')); 239 240 // We use another factory with the same cache, to simulate another request and skip the in memory 241 $factory = new MetadataFactory($driver); 242 $factory->setCache($cache); 243 $factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject'); 244 $factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject'); 245 $this->assertNull($factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject')); 246 } 247 248 public function testNotFoundMetadataIsNotCachedInDebug() 249 { 250 $driver = $this->createMock('Metadata\Driver\DriverInterface'); 251 $driver 252 ->expects($this->exactly(2)) 253 ->method('loadMetadataForClass') 254 ->will($this->returnValue(null)) 255 ; 256 257 $cachedMetadata = null; 258 $cache = $this->createMock('Metadata\Cache\CacheInterface'); 259 $cache 260 ->expects($this->any()) 261 ->method('load') 262 ->with($this->equalTo('Metadata\Tests\Fixtures\TestObject')) 263 ->will($this->returnValue(null)) 264 ; 265 $cache 266 ->expects($this->never()) 267 ->method('put') 268 ; 269 270 $factory = new MetadataFactory($driver, 'Metadata\ClassHierarchyMetadata', true); 271 $factory->setCache($cache); 272 $factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject'); 273 $this->assertNull($factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject')); 274 275 // We use another factory with the same cache, to simulate another request and skip the in memory 276 $factory = new MetadataFactory($driver, 'Metadata\ClassHierarchyMetadata', true); 277 $factory->setCache($cache); 278 $factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject'); 279 $this->assertNull($factory->getMetadataForClass('Metadata\Tests\Fixtures\TestObject')); 280 } 281} 282