/tests/Doctrine/Tests/ORM/Cache/FileLockRegionTest.php

https://github.com/jaikdean/doctrine2 · PHP · 295 lines · 209 code · 61 blank · 25 comment · 2 complexity · 589d4e4c725825e81c63bc3832e5b068 MD5 · raw file

  1. <?php
  2. declare(strict_types=1);
  3. namespace Doctrine\Tests\ORM\Cache;
  4. use Doctrine\ORM\Cache\CacheKey;
  5. use Doctrine\ORM\Cache\ConcurrentRegion;
  6. use Doctrine\ORM\Cache\Lock;
  7. use Doctrine\ORM\Cache\Region\DefaultRegion;
  8. use Doctrine\ORM\Cache\Region\FileLockRegion;
  9. use Doctrine\Tests\Mocks\CacheEntryMock;
  10. use Doctrine\Tests\Mocks\CacheKeyMock;
  11. use RecursiveDirectoryIterator;
  12. use RecursiveIteratorIterator;
  13. use ReflectionMethod;
  14. use ReflectionProperty;
  15. use const E_WARNING;
  16. use function file_put_contents;
  17. use function is_dir;
  18. use function restore_error_handler;
  19. use function rmdir;
  20. use function set_error_handler;
  21. use function str_repeat;
  22. use function sys_get_temp_dir;
  23. use function uniqid;
  24. use function unlink;
  25. /**
  26. * @group DDC-2183
  27. */
  28. class FileLockRegionTest extends AbstractRegionTest
  29. {
  30. /** @var ConcurrentRegion */
  31. protected $region;
  32. /** @var string */
  33. protected $directory;
  34. /**
  35. * {@inheritDoc}
  36. */
  37. public function tearDown() : void
  38. {
  39. $this->cleanTestDirectory($this->directory);
  40. }
  41. /**
  42. * @return string
  43. */
  44. private function getFileName(ConcurrentRegion $region, CacheKey $key)
  45. {
  46. $reflection = new ReflectionMethod($region, 'getLockFileName');
  47. $reflection->setAccessible(true);
  48. return $reflection->invoke($region, $key);
  49. }
  50. protected function createRegion()
  51. {
  52. $this->directory = sys_get_temp_dir() . '/doctrine_lock_' . uniqid();
  53. $region = new DefaultRegion('concurren_region_test', $this->cache);
  54. return new FileLockRegion($region, $this->directory, 60);
  55. }
  56. public function testGetRegionName() : void
  57. {
  58. self::assertEquals('concurren_region_test', $this->region->getName());
  59. }
  60. public function testLockAndUnlock() : void
  61. {
  62. $key = new CacheKeyMock('key');
  63. $entry = new CacheEntryMock(['foo' => 'bar']);
  64. $file = $this->getFileName($this->region, $key);
  65. self::assertFalse($this->region->contains($key));
  66. self::assertTrue($this->region->put($key, $entry));
  67. self::assertTrue($this->region->contains($key));
  68. $lock = $this->region->lock($key);
  69. self::assertFileExists($file);
  70. self::assertInstanceOf(Lock::class, $lock);
  71. self::assertStringEqualsFile($file, $lock->value);
  72. // should be not available after lock
  73. self::assertFalse($this->region->contains($key));
  74. self::assertNull($this->region->get($key));
  75. self::assertTrue($this->region->unlock($key, $lock));
  76. self::assertFileNotExists($file);
  77. }
  78. public function testLockWithExistingLock() : void
  79. {
  80. $key = new CacheKeyMock('key');
  81. $entry = new CacheEntryMock(['foo' => 'bar']);
  82. $file = $this->getFileName($this->region, $key);
  83. self::assertFalse($this->region->contains($key));
  84. self::assertTrue($this->region->put($key, $entry));
  85. self::assertTrue($this->region->contains($key));
  86. file_put_contents($file, 'foo');
  87. self::assertFileExists($file);
  88. self::assertStringEqualsFile($file, 'foo');
  89. self::assertNull($this->region->lock($key));
  90. self::assertStringEqualsFile($file, 'foo');
  91. self::assertFileExists($file);
  92. // should be not available
  93. self::assertFalse($this->region->contains($key));
  94. self::assertNull($this->region->get($key));
  95. }
  96. public function testUnlockWithExistingLock() : void
  97. {
  98. $key = new CacheKeyMock('key');
  99. $entry = new CacheEntryMock(['foo' => 'bar']);
  100. $file = $this->getFileName($this->region, $key);
  101. self::assertFalse($this->region->contains($key));
  102. self::assertTrue($this->region->put($key, $entry));
  103. self::assertTrue($this->region->contains($key));
  104. self::assertInstanceOf(Lock::class, $lock = $this->region->lock($key));
  105. self::assertStringEqualsFile($file, $lock->value);
  106. self::assertFileExists($file);
  107. // change the lock
  108. file_put_contents($file, 'foo');
  109. self::assertFileExists($file);
  110. self::assertStringEqualsFile($file, 'foo');
  111. //try to unlock
  112. self::assertFalse($this->region->unlock($key, $lock));
  113. self::assertStringEqualsFile($file, 'foo');
  114. self::assertFileExists($file);
  115. // should be not available
  116. self::assertFalse($this->region->contains($key));
  117. self::assertNull($this->region->get($key));
  118. }
  119. public function testPutWithExistingLock() : void
  120. {
  121. $key = new CacheKeyMock('key');
  122. $entry = new CacheEntryMock(['foo' => 'bar']);
  123. $file = $this->getFileName($this->region, $key);
  124. self::assertFalse($this->region->contains($key));
  125. self::assertTrue($this->region->put($key, $entry));
  126. self::assertTrue($this->region->contains($key));
  127. // create lock
  128. file_put_contents($file, 'foo');
  129. self::assertFileExists($file);
  130. self::assertStringEqualsFile($file, 'foo');
  131. self::assertFalse($this->region->contains($key));
  132. self::assertFalse($this->region->put($key, $entry));
  133. self::assertFalse($this->region->contains($key));
  134. self::assertFileExists($file);
  135. self::assertStringEqualsFile($file, 'foo');
  136. }
  137. public function testLockedEvict() : void
  138. {
  139. $key = new CacheKeyMock('key');
  140. $entry = new CacheEntryMock(['foo' => 'bar']);
  141. $file = $this->getFileName($this->region, $key);
  142. self::assertFalse($this->region->contains($key));
  143. self::assertTrue($this->region->put($key, $entry));
  144. self::assertTrue($this->region->contains($key));
  145. self::assertInstanceOf(Lock::class, $lock = $this->region->lock($key));
  146. self::assertStringEqualsFile($file, $lock->value);
  147. self::assertFileExists($file);
  148. self::assertFalse($this->region->contains($key));
  149. self::assertTrue($this->region->evict($key));
  150. self::assertFalse($this->region->contains($key));
  151. self::assertFileNotExists($file);
  152. }
  153. public function testLockedEvictAll() : void
  154. {
  155. $key1 = new CacheKeyMock('key1');
  156. $entry1 = new CacheEntryMock(['foo1' => 'bar1']);
  157. $file1 = $this->getFileName($this->region, $key1);
  158. $key2 = new CacheKeyMock('key2');
  159. $entry2 = new CacheEntryMock(['foo2' => 'bar2']);
  160. $file2 = $this->getFileName($this->region, $key2);
  161. self::assertFalse($this->region->contains($key1));
  162. self::assertTrue($this->region->put($key1, $entry1));
  163. self::assertTrue($this->region->contains($key1));
  164. self::assertFalse($this->region->contains($key2));
  165. self::assertTrue($this->region->put($key2, $entry2));
  166. self::assertTrue($this->region->contains($key2));
  167. self::assertInstanceOf(Lock::class, $lock1 = $this->region->lock($key1));
  168. self::assertInstanceOf(Lock::class, $lock2 = $this->region->lock($key2));
  169. self::assertStringEqualsFile($file2, $lock2->value);
  170. self::assertStringEqualsFile($file1, $lock1->value);
  171. self::assertFileExists($file1);
  172. self::assertFileExists($file2);
  173. self::assertTrue($this->region->evictAll());
  174. self::assertFileNotExists($file1);
  175. self::assertFileNotExists($file2);
  176. self::assertFalse($this->region->contains($key1));
  177. self::assertFalse($this->region->contains($key2));
  178. }
  179. public function testLockLifetime() : void
  180. {
  181. $key = new CacheKeyMock('key');
  182. $entry = new CacheEntryMock(['foo' => 'bar']);
  183. $file = $this->getFileName($this->region, $key);
  184. $property = new ReflectionProperty($this->region, 'lockLifetime');
  185. $property->setAccessible(true);
  186. $property->setValue($this->region, -10);
  187. self::assertFalse($this->region->contains($key));
  188. self::assertTrue($this->region->put($key, $entry));
  189. self::assertTrue($this->region->contains($key));
  190. self::assertInstanceOf(Lock::class, $lock = $this->region->lock($key));
  191. self::assertStringEqualsFile($file, $lock->value);
  192. self::assertFileExists($file);
  193. // outdated lock should be removed
  194. self::assertTrue($this->region->contains($key));
  195. self::assertNotNull($this->region->get($key));
  196. self::assertFileNotExists($file);
  197. }
  198. /**
  199. * @group 1072
  200. * @group DDC-3191
  201. */
  202. public function testHandlesScanErrorsGracefullyOnEvictAll() : void
  203. {
  204. $region = $this->createRegion();
  205. $reflectionDirectory = new ReflectionProperty($region, 'directory');
  206. $reflectionDirectory->setAccessible(true);
  207. $reflectionDirectory->setValue($region, str_repeat('a', 10000));
  208. set_error_handler(static function () {
  209. }, E_WARNING);
  210. self::assertTrue($region->evictAll());
  211. restore_error_handler();
  212. }
  213. /**
  214. * @param string|null $path directory to clean
  215. */
  216. private function cleanTestDirectory($path)
  217. {
  218. $path = $path ?: $this->directory;
  219. if (! is_dir($path)) {
  220. return;
  221. }
  222. $directoryIterator = new RecursiveIteratorIterator(
  223. new RecursiveDirectoryIterator($path),
  224. RecursiveIteratorIterator::CHILD_FIRST
  225. );
  226. foreach ($directoryIterator as $file) {
  227. if ($file->isFile()) {
  228. @unlink($file->getRealPath());
  229. } elseif ($file->getRealPath() !== false) {
  230. @rmdir($file->getRealPath());
  231. }
  232. }
  233. }
  234. }