FileCacheManagerTest.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. <?php
  2. declare(strict_types=1);
  3. /*
  4. * This file is part of PHP CS Fixer.
  5. *
  6. * (c) Fabien Potencier <fabien@symfony.com>
  7. * Dariusz Rumiński <dariusz.ruminski@gmail.com>
  8. *
  9. * This source file is subject to the MIT license that is bundled
  10. * with this source code in the file LICENSE.
  11. */
  12. namespace PhpCsFixer\Tests\Cache;
  13. use PhpCsFixer\AccessibleObject\AccessibleObject;
  14. use PhpCsFixer\Cache\CacheInterface;
  15. use PhpCsFixer\Cache\CacheManagerInterface;
  16. use PhpCsFixer\Cache\DirectoryInterface;
  17. use PhpCsFixer\Cache\FileCacheManager;
  18. use PhpCsFixer\Cache\FileHandlerInterface;
  19. use PhpCsFixer\Cache\SignatureInterface;
  20. use PhpCsFixer\Hasher;
  21. use PhpCsFixer\Tests\TestCase;
  22. /**
  23. * @author Andreas Möller <am@localheinz.com>
  24. *
  25. * @internal
  26. *
  27. * @covers \PhpCsFixer\Cache\FileCacheManager
  28. */
  29. final class FileCacheManagerTest extends TestCase
  30. {
  31. public function testIsFinal(): void
  32. {
  33. $reflection = new \ReflectionClass(FileCacheManager::class);
  34. self::assertTrue($reflection->isFinal());
  35. }
  36. public function testImplementsCacheManagerInterface(): void
  37. {
  38. $reflection = new \ReflectionClass(FileCacheManager::class);
  39. self::assertTrue($reflection->implementsInterface(CacheManagerInterface::class));
  40. }
  41. public function testCreatesCacheIfHandlerReturnedNoCache(): void
  42. {
  43. $signature = $this->createSignatureDouble(false);
  44. $handler = $this->createFileHandlerDouble(null);
  45. $manager = new FileCacheManager($handler, $signature);
  46. unset($manager);
  47. self::assertSame(1, AccessibleObject::create($handler)->writeCallCount);
  48. }
  49. public function testCreatesCacheIfCachedSignatureIsDifferent(): void
  50. {
  51. $cachedSignature = $this->createSignatureDouble(false);
  52. $signature = $this->createSignatureDouble(false);
  53. $cache = $this->createCacheDouble($cachedSignature);
  54. $handler = $this->createFileHandlerDouble($cache);
  55. $manager = new FileCacheManager($handler, $signature);
  56. unset($manager);
  57. self::assertSame(1, AccessibleObject::create($handler)->writeCallCount);
  58. }
  59. public function testUsesCacheIfCachedSignatureIsEqualAndNoFileWasUpdated(): void
  60. {
  61. $cachedSignature = $this->createSignatureDouble(true);
  62. $signature = $this->createSignatureDouble(true);
  63. $cache = $this->createCacheDouble($cachedSignature);
  64. $handler = $this->createFileHandlerDouble($cache);
  65. $manager = new FileCacheManager($handler, $signature);
  66. unset($manager);
  67. self::assertSame(0, AccessibleObject::create($handler)->writeCallCount);
  68. }
  69. public function testNeedFixingReturnsTrueIfCacheHasNoHash(): void
  70. {
  71. $cachedSignature = $this->createSignatureDouble(true);
  72. $signature = $this->createSignatureDouble(true);
  73. $cache = $this->createCacheDouble($cachedSignature);
  74. $handler = $this->createFileHandlerDouble($cache, $this->getFile());
  75. $manager = new FileCacheManager($handler, $signature);
  76. self::assertTrue($manager->needFixing('hello.php', '<?php echo "Hello!"'));
  77. }
  78. public function testNeedFixingReturnsTrueIfCachedHashIsDifferent(): void
  79. {
  80. $file = 'hello.php';
  81. $cachedSignature = $this->createSignatureDouble(true);
  82. $signature = $this->createSignatureDouble(true);
  83. $cache = $this->createCacheDouble($cachedSignature, [$file => Hasher::calculate('<?php echo "Hello, old world!";')]);
  84. $handler = $this->createFileHandlerDouble($cache, $this->getFile());
  85. $manager = new FileCacheManager($handler, $signature);
  86. self::assertTrue($manager->needFixing($file, '<?php echo "Hello, new world!";'));
  87. }
  88. public function testNeedFixingReturnsFalseIfCachedHashIsIdentical(): void
  89. {
  90. $file = 'hello.php';
  91. $fileContent = '<?php echo "Hello!"';
  92. $cachedSignature = $this->createSignatureDouble(true);
  93. $signature = $this->createSignatureDouble(true);
  94. $cache = $this->createCacheDouble($cachedSignature, [$file => Hasher::calculate($fileContent)]);
  95. $handler = $this->createFileHandlerDouble($cache, $this->getFile());
  96. $manager = new FileCacheManager($handler, $signature);
  97. self::assertFalse($manager->needFixing($file, $fileContent));
  98. }
  99. public function testNeedFixingUsesRelativePathToFile(): void
  100. {
  101. $cacheFile = $this->getFile();
  102. $file = '/foo/bar/baz/src/hello.php';
  103. $relativePathToFile = 'src/hello.php';
  104. $directory = $this->createDirectoryDouble($relativePathToFile);
  105. $cachedSignature = $this->createSignatureDouble(true);
  106. $signature = $this->createSignatureDouble(true);
  107. $cache = $this->createCacheDouble($cachedSignature, [$relativePathToFile => Hasher::calculate('<?php echo "Old!"')]);
  108. $handler = $this->createFileHandlerDouble($cache, $this->getFile());
  109. $manager = new FileCacheManager($handler, $signature, false, $directory);
  110. self::assertTrue($manager->needFixing($file, '<?php echo "New!"'));
  111. }
  112. public function testSetFileSetsHashOfFileContent(): void
  113. {
  114. $cacheFile = $this->getFile();
  115. $file = 'hello.php';
  116. $fileContent = '<?php echo "Hello!"';
  117. $cachedSignature = $this->createSignatureDouble(true);
  118. $signature = $this->createSignatureDouble(true);
  119. $cache = $this->createCacheDouble($cachedSignature);
  120. $handler = $this->createFileHandlerDouble($cache, $cacheFile);
  121. $manager = new FileCacheManager($handler, $signature);
  122. self::assertFalse($cache->has($file));
  123. $manager->setFile($file, $fileContent);
  124. unset($manager);
  125. self::assertTrue($cache->has($file));
  126. self::assertSame(Hasher::calculate($fileContent), $cache->get($file));
  127. self::assertSame(1, AccessibleObject::create($handler)->writeCallCount);
  128. }
  129. public function testSetFileSetsHashOfFileContentDuringDryRunIfCacheHasNoHash(): void
  130. {
  131. $isDryRun = true;
  132. $cacheFile = $this->getFile();
  133. $file = 'hello.php';
  134. $fileContent = '<?php echo "Hello!"';
  135. $cachedSignature = $this->createSignatureDouble(true);
  136. $signature = $this->createSignatureDouble(true);
  137. $cache = $this->createCacheDouble($cachedSignature);
  138. $handler = $this->createFileHandlerDouble($cache, $cacheFile);
  139. self::assertFalse($cache->has($file));
  140. $manager = new FileCacheManager($handler, $signature, $isDryRun);
  141. $manager->setFile($file, $fileContent);
  142. self::assertTrue($cache->has($file));
  143. self::assertSame(Hasher::calculate($fileContent), $cache->get($file));
  144. }
  145. public function testSetFileClearsHashDuringDryRunIfCachedHashIsDifferent(): void
  146. {
  147. $isDryRun = true;
  148. $cacheFile = $this->getFile();
  149. $file = 'hello.php';
  150. $fileContent = '<?php echo "Hello!"';
  151. $previousFileContent = '<?php echo "Hello, world!"';
  152. $cachedSignature = $this->createSignatureDouble(true);
  153. $signature = $this->createSignatureDouble(true);
  154. $cache = $this->createCacheDouble($cachedSignature, [$file => Hasher::calculate($previousFileContent)]);
  155. $handler = $this->createFileHandlerDouble($cache, $cacheFile);
  156. $manager = new FileCacheManager($handler, $signature, $isDryRun);
  157. $manager->setFile($file, $fileContent);
  158. self::assertFalse($cache->has($file));
  159. }
  160. public function testSetFileUsesRelativePathToFile(): void
  161. {
  162. $cacheFile = $this->getFile();
  163. $file = '/foo/bar/baz/src/hello.php';
  164. $relativePathToFile = 'src/hello.php';
  165. $fileContent = '<?php echo "Hello!"';
  166. $directory = $this->createDirectoryDouble($relativePathToFile);
  167. $cachedSignature = $this->createSignatureDouble(true);
  168. $signature = $this->createSignatureDouble(true);
  169. $cache = $this->createCacheDouble($cachedSignature);
  170. $handler = $this->createFileHandlerDouble($cache, $cacheFile);
  171. $manager = new FileCacheManager($handler, $signature, false, $directory);
  172. $manager->setFile($file, $fileContent);
  173. self::assertTrue($cache->has($relativePathToFile));
  174. self::assertSame(Hasher::calculate($fileContent), $cache->get($relativePathToFile));
  175. }
  176. private function getFile(): string
  177. {
  178. return __DIR__.'/../Fixtures/.php_cs.empty-cache';
  179. }
  180. private function createDirectoryDouble(string $relativePath): DirectoryInterface
  181. {
  182. return new class($relativePath) implements DirectoryInterface {
  183. private string $relativePath;
  184. public function __construct(string $relativePath)
  185. {
  186. $this->relativePath = $relativePath;
  187. }
  188. public function getRelativePathTo(string $file): string
  189. {
  190. return $this->relativePath;
  191. }
  192. };
  193. }
  194. private function createSignatureDouble(bool $isEqual): SignatureInterface
  195. {
  196. return new class($isEqual) implements SignatureInterface {
  197. private bool $isEqual;
  198. public function __construct(bool $isEqual)
  199. {
  200. $this->isEqual = $isEqual;
  201. }
  202. public function getPhpVersion(): string
  203. {
  204. throw new \LogicException('Not implemented.');
  205. }
  206. public function getFixerVersion(): string
  207. {
  208. throw new \LogicException('Not implemented.');
  209. }
  210. public function getIndent(): string
  211. {
  212. throw new \LogicException('Not implemented.');
  213. }
  214. public function getLineEnding(): string
  215. {
  216. throw new \LogicException('Not implemented.');
  217. }
  218. public function getRules(): array
  219. {
  220. throw new \LogicException('Not implemented.');
  221. }
  222. public function equals(SignatureInterface $signature): bool
  223. {
  224. return $this->isEqual;
  225. }
  226. };
  227. }
  228. /**
  229. * @param array<string, string> $fileMap
  230. */
  231. private function createCacheDouble(SignatureInterface $signature, array $fileMap = []): CacheInterface
  232. {
  233. return new class($signature, $fileMap) implements CacheInterface {
  234. private SignatureInterface $signature;
  235. /** @var array<string, string> */
  236. private array $fileMap;
  237. /**
  238. * @param array<string, string> $fileMap
  239. */
  240. public function __construct(SignatureInterface $signature, array $fileMap)
  241. {
  242. $this->signature = $signature;
  243. $this->fileMap = $fileMap;
  244. }
  245. public function getSignature(): SignatureInterface
  246. {
  247. return $this->signature;
  248. }
  249. public function has(string $file): bool
  250. {
  251. return isset($this->fileMap[$file]);
  252. }
  253. public function get(string $file): string
  254. {
  255. return $this->fileMap[$file];
  256. }
  257. public function set(string $file, string $hash): void
  258. {
  259. $this->fileMap[$file] = $hash;
  260. }
  261. public function clear(string $file): void
  262. {
  263. unset($this->fileMap[$file]);
  264. }
  265. public function toJson(): string
  266. {
  267. throw new \LogicException('Not implemented.');
  268. }
  269. };
  270. }
  271. private function createFileHandlerDouble(?CacheInterface $cache, ?string $file = null, ?string $signature = null): FileHandlerInterface
  272. {
  273. return new class($cache, $file, $signature) implements FileHandlerInterface {
  274. private ?CacheInterface $cache;
  275. private ?string $file;
  276. private ?string $signature;
  277. private int $writeCallCount = 0;
  278. public function __construct(?CacheInterface $cache, ?string $file, ?string $signature)
  279. {
  280. $this->cache = $cache;
  281. $this->file = $file;
  282. $this->signature = $signature;
  283. }
  284. public function getFile(): string
  285. {
  286. return $this->file;
  287. }
  288. public function read(): ?CacheInterface
  289. {
  290. return $this->cache;
  291. }
  292. public function write(CacheInterface $cache): void
  293. {
  294. ++$this->writeCallCount;
  295. if (null !== $this->signature) {
  296. TestCase::assertSame($this->signature, $cache->getSignature());
  297. }
  298. }
  299. };
  300. }
  301. }