dc43a806a72e2f104a84e9011e38ec063887aa0c
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Unit / Cache / Backend / FileBackendTest.php
1 <?php
2 declare(strict_types = 1);
3
4 namespace TYPO3\CMS\Core\Tests\Unit\Cache\Backend;
5
6 /*
7 * This file is part of the TYPO3 CMS project.
8 *
9 * It is free software; you can redistribute it and/or modify it under
10 * the terms of the GNU General Public License, either version 2
11 * of the License, or any later version.
12 *
13 * For the full copyright and license information, please read the
14 * LICENSE.txt file that was distributed with this source code.
15 *
16 * The TYPO3 project - inspiring people to share!
17 */
18
19 use org\bovigo\vfs\vfsStreamDirectory;
20 use org\bovigo\vfs\vfsStreamWrapper;
21 use TYPO3\CMS\Core\Cache\Backend\FileBackend;
22 use TYPO3\CMS\Core\Cache\Exception;
23 use TYPO3\CMS\Core\Cache\Exception\InvalidDataException;
24 use TYPO3\CMS\Core\Cache\Frontend\AbstractFrontend;
25 use TYPO3\CMS\Core\Cache\Frontend\PhpFrontend;
26 use TYPO3\CMS\Core\Utility\GeneralUtility;
27 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
28
29 /**
30 * Testcase for the File cache backend
31 */
32 class FileBackendTest extends UnitTestCase
33 {
34 /**
35 * Sets up this testcase
36 * @throws \org\bovigo\vfs\vfsStreamException
37 */
38 protected function setUp(): void
39 {
40 if (!class_exists(vfsStreamWrapper::class)) {
41 $this->markTestSkipped('File backend tests are not available with this phpunit version.');
42 }
43
44 vfsStreamWrapper::register();
45 vfsStreamWrapper::setRoot(new vfsStreamDirectory('Foo'));
46 }
47
48 /**
49 * @test
50 * @throws Exception
51 */
52 public function setCacheDirectoryThrowsExceptionOnNonWritableDirectory(): void
53 {
54 $this->expectException(Exception::class);
55 $this->expectExceptionCode(1303669848);
56
57 $mockCache = $this->createMock(AbstractFrontend::class);
58
59 $backend = $this->getMockBuilder(FileBackend::class)
60 ->setMethods(['dummy'])
61 ->disableOriginalConstructor()
62 ->getMock();
63 $backend->setCacheDirectory('http://localhost/');
64
65 $backend->setCache($mockCache);
66 }
67
68 /**
69 * @test
70 */
71 public function setCacheDirectoryAllowsAbsolutePathWithoutTrailingSlash(): void
72 {
73 $backend = $this->getAccessibleMock(FileBackend::class, ['dummy'], [], '', false);
74 $backend->_set('cacheIdentifier', 'test');
75 $backend->setCacheDirectory('/tmp/foo');
76 $this->assertEquals('/tmp/foo/test/', $backend->_get('temporaryCacheDirectory'));
77 }
78
79 /**
80 * @test
81 */
82 public function setCacheDirectoryAllowsAbsolutePathWithTrailingSlash(): void
83 {
84 $backend = $this->getAccessibleMock(FileBackend::class, ['dummy'], [], '', false);
85 $backend->_set('cacheIdentifier', 'test');
86 $backend->setCacheDirectory('/tmp/foo/');
87 $this->assertEquals('/tmp/foo/test/', $backend->_get('temporaryCacheDirectory'));
88 }
89
90 /**
91 * @test
92 */
93 public function setCacheDirectoryAllowsRelativePathWithoutTrailingSlash(): void
94 {
95 $backend = $this->getAccessibleMock(FileBackend::class, ['dummy'], [], '', false);
96 $backend->_set('cacheIdentifier', 'test');
97 $backend->setCacheDirectory('tmp/foo');
98 // get PATH_site without trailing slash
99 $path = GeneralUtility::fixWindowsFilePath(realpath(PATH_site));
100 $this->assertEquals($path . '/tmp/foo/test/', $backend->_get('temporaryCacheDirectory'));
101 }
102
103 /**
104 * @test
105 */
106 public function setCacheDirectoryAllowsRelativePathWithTrailingSlash(): void
107 {
108 $backend = $this->getAccessibleMock(FileBackend::class, ['dummy'], [], '', false);
109 $backend->_set('cacheIdentifier', 'test');
110 $backend->setCacheDirectory('tmp/foo/');
111 // get PATH_site without trailing slash
112 $path = GeneralUtility::fixWindowsFilePath(realpath(PATH_site));
113 $this->assertEquals($path . '/tmp/foo/test/', $backend->_get('temporaryCacheDirectory'));
114 }
115
116 /**
117 * @test
118 */
119 public function setCacheDirectoryAllowsRelativeDottedPathWithoutTrailingSlash(): void
120 {
121 $backend = $this->getAccessibleMock(FileBackend::class, ['dummy'], [], '', false);
122 $backend->_set('cacheIdentifier', 'test');
123 $backend->setCacheDirectory('../tmp/foo');
124 // get PATH_site without trailing slash
125 $path = GeneralUtility::fixWindowsFilePath(realpath(PATH_site));
126 $this->assertEquals($path . '/../tmp/foo/test/', $backend->_get('temporaryCacheDirectory'));
127 }
128
129 /**
130 * @test
131 */
132 public function setCacheDirectoryAllowsRelativeDottedPathWithTrailingSlash(): void
133 {
134 $backend = $this->getAccessibleMock(FileBackend::class, ['dummy'], [], '', false);
135 $backend->_set('cacheIdentifier', 'test');
136 $backend->setCacheDirectory('../tmp/foo/');
137 // get PATH_site without trailing slash
138 $path = GeneralUtility::fixWindowsFilePath(realpath(PATH_site));
139 $this->assertEquals($path . '/../tmp/foo/test/', $backend->_get('temporaryCacheDirectory'));
140 }
141
142 /**
143 * @test
144 */
145 public function setCacheDirectoryAllowsAbsoluteDottedPathWithoutTrailingSlash(): void
146 {
147 $backend = $this->getAccessibleMock(FileBackend::class, ['dummy'], [], '', false);
148 $backend->_set('cacheIdentifier', 'test');
149 $backend->setCacheDirectory('/tmp/../foo');
150 $this->assertEquals('/tmp/../foo/test/', $backend->_get('temporaryCacheDirectory'));
151 }
152
153 /**
154 * @test
155 */
156 public function setCacheDirectoryAllowsAbsoluteDottedPathWithTrailingSlash(): void
157 {
158 $backend = $this->getAccessibleMock(FileBackend::class, ['dummy'], [], '', false);
159 $backend->_set('cacheIdentifier', 'test');
160 $backend->setCacheDirectory('/tmp/../foo/');
161 $this->assertEquals('/tmp/../foo/test/', $backend->_get('temporaryCacheDirectory'));
162 }
163
164 /**
165 * @test
166 * @throws Exception
167 */
168 public function getCacheDirectoryReturnsTheCurrentCacheDirectory(): void
169 {
170 $mockCache = $this->createMock(AbstractFrontend::class);
171 $mockCache->expects($this->any())->method('getIdentifier')->will($this->returnValue('SomeCache'));
172
173 $backend = $this->getMockBuilder(FileBackend::class)
174 ->setMethods(['dummy'])
175 ->disableOriginalConstructor()
176 ->getMock();
177 $backend->setCacheDirectory('vfs://Foo/');
178 $backend->setCache($mockCache);
179
180 $this->assertEquals('vfs://Foo/cache/data/SomeCache/', $backend->getCacheDirectory());
181 }
182
183 /**
184 * @test
185 * @throws Exception
186 */
187 public function aDedicatedCacheDirectoryIsUsedForCodeCaches(): void
188 {
189 $mockCache = $this->createMock(PhpFrontend::class);
190 $mockCache->expects($this->any())->method('getIdentifier')->will($this->returnValue('SomeCache'));
191
192 $backend = $this->getMockBuilder(FileBackend::class)
193 ->setMethods(['dummy'])
194 ->disableOriginalConstructor()
195 ->getMock();
196 $backend->setCacheDirectory('vfs://Foo/');
197 $backend->setCache($mockCache);
198
199 $this->assertEquals('vfs://Foo/cache/code/SomeCache/', $backend->getCacheDirectory());
200 }
201
202 /**
203 * @test
204 * @throws Exception
205 */
206 public function setThrowsExceptionIfDataIsNotAString(): void
207 {
208 $this->expectException(InvalidDataException::class);
209 $this->expectExceptionCode(1204481674);
210
211 $mockCache = $this->createMock(AbstractFrontend::class);
212
213 $backend = $this->getMockBuilder(FileBackend::class)
214 ->setMethods(['dummy'])
215 ->disableOriginalConstructor()
216 ->getMock();
217 $backend->setCacheDirectory('vfs://Foo/');
218 $backend->setCache($mockCache);
219
220 $backend->set('some identifier', ['not a string']);
221 }
222
223 /**
224 * @test
225 * @throws Exception
226 */
227 public function setReallySavesToTheSpecifiedDirectory(): void
228 {
229 $mockCache = $this->createMock(AbstractFrontend::class);
230 $mockCache->expects($this->atLeastOnce())->method('getIdentifier')->will($this->returnValue('UnitTestCache'));
231
232 $data = 'some data' . microtime();
233 $entryIdentifier = 'BackendFileTest';
234 $pathAndFilename = 'vfs://Foo/cache/data/UnitTestCache/' . $entryIdentifier;
235
236 $backend = $this->getMockBuilder(FileBackend::class)
237 ->setMethods(['dummy'])
238 ->disableOriginalConstructor()
239 ->getMock();
240 $backend->setCacheDirectory('vfs://Foo/');
241 $backend->setCache($mockCache);
242
243 $backend->set($entryIdentifier, $data);
244
245 $this->assertFileExists($pathAndFilename);
246 $retrievedData = file_get_contents($pathAndFilename, false, null, 0, \strlen($data));
247 $this->assertEquals($data, $retrievedData);
248 }
249
250 /**
251 * @test
252 * @throws Exception
253 */
254 public function setOverwritesAnAlreadyExistingCacheEntryForTheSameIdentifier(): void
255 {
256 $mockCache = $this->createMock(AbstractFrontend::class);
257 $mockCache->expects($this->atLeastOnce())->method('getIdentifier')->will($this->returnValue('UnitTestCache'));
258
259 $data1 = 'some data' . microtime();
260 $data2 = 'some data' . microtime();
261 $entryIdentifier = 'BackendFileRemoveBeforeSetTest';
262
263 $backend = $this->getMockBuilder(FileBackend::class)
264 ->setMethods(['dummy'])
265 ->disableOriginalConstructor()
266 ->getMock();
267 $backend->setCacheDirectory('vfs://Foo/');
268 $backend->setCache($mockCache);
269
270 $backend->set($entryIdentifier, $data1, [], 500);
271 $backend->set($entryIdentifier, $data2, [], 200);
272
273 $pathAndFilename = 'vfs://Foo/cache/data/UnitTestCache/' . $entryIdentifier;
274 $this->assertFileExists($pathAndFilename);
275 $retrievedData = file_get_contents($pathAndFilename, false, null, 0, \strlen($data2));
276 $this->assertEquals($data2, $retrievedData);
277 }
278
279 /**
280 * @test
281 * @throws Exception
282 */
283 public function setAlsoSavesSpecifiedTags(): void
284 {
285 $mockCache = $this->createMock(AbstractFrontend::class);
286 $mockCache->expects($this->atLeastOnce())->method('getIdentifier')->will($this->returnValue('UnitTestCache'));
287
288 $data = 'some data' . microtime();
289 $entryIdentifier = 'BackendFileRemoveBeforeSetTest';
290
291 $backend = $this->getMockBuilder(FileBackend::class)
292 ->setMethods(['dummy'])
293 ->disableOriginalConstructor()
294 ->getMock();
295 $backend->setCacheDirectory('vfs://Foo/');
296 $backend->setCache($mockCache);
297
298 $backend->set($entryIdentifier, $data, ['Tag1', 'Tag2']);
299
300 $pathAndFilename = 'vfs://Foo/cache/data/UnitTestCache/' . $entryIdentifier;
301 $this->assertFileExists($pathAndFilename);
302 $retrievedData = file_get_contents(
303 $pathAndFilename,
304 false,
305 null,
306 \strlen($data) + FileBackend::EXPIRYTIME_LENGTH,
307 9
308 );
309 $this->assertEquals('Tag1 Tag2', $retrievedData);
310 }
311
312 /**
313 * @test
314 * @throws Exception
315 */
316 public function setCacheDetectsAndLoadsAFrozenCache(): void
317 {
318 $mockCache = $this->createMock(AbstractFrontend::class);
319 $mockCache->expects($this->atLeastOnce())->method('getIdentifier')->will($this->returnValue('UnitTestCache'));
320
321 $data = 'some data' . microtime();
322 $entryIdentifier = 'BackendFileTest';
323
324 $backend = $this->getMockBuilder(FileBackend::class)
325 ->setMethods(['dummy'])
326 ->disableOriginalConstructor()
327 ->getMock();
328 $backend->setCacheDirectory('vfs://Foo/');
329 $backend->setCache($mockCache);
330
331 $backend->set($entryIdentifier, $data, ['Tag1', 'Tag2']);
332
333 $backend->freeze();
334
335 unset($backend);
336
337 $backend = $this->getMockBuilder(FileBackend::class)
338 ->setMethods(['dummy'])
339 ->disableOriginalConstructor()
340 ->getMock();
341 $backend->setCacheDirectory('vfs://Foo/');
342 $backend->setCache($mockCache);
343
344 $this->assertTrue($backend->isFrozen());
345 $this->assertEquals($data, $backend->get($entryIdentifier));
346 }
347
348 /**
349 * @test
350 * @throws Exception
351 */
352 public function getReturnsContentOfTheCorrectCacheFile(): void
353 {
354 $mockCache = $this->createMock(AbstractFrontend::class);
355 $mockCache->expects($this->atLeastOnce())->method('getIdentifier')->will($this->returnValue('UnitTestCache'));
356
357 $backend = $this->getMockBuilder(FileBackend::class)
358 ->setMethods(['setTag'])
359 ->disableOriginalConstructor()
360 ->getMock();
361 $backend->setCacheDirectory('vfs://Foo/');
362 $backend->setCache($mockCache);
363
364 $entryIdentifier = 'BackendFileTest';
365
366 $data = 'some data' . microtime();
367 $backend->set($entryIdentifier, $data, [], 500);
368
369 $data = 'some other data' . microtime();
370 $backend->set($entryIdentifier, $data, [], 100);
371
372 $loadedData = $backend->get($entryIdentifier);
373 $this->assertEquals($data, $loadedData);
374 }
375
376 /**
377 * @test
378 * @throws Exception
379 */
380 public function getReturnsFalseForExpiredEntries(): void
381 {
382 $mockCache = $this->createMock(AbstractFrontend::class);
383 $mockCache->expects($this->atLeastOnce())->method('getIdentifier')->will($this->returnValue('UnitTestCache'));
384
385 $backend = $this->getMockBuilder(FileBackend::class)
386 ->setMethods(['isCacheFileExpired'])
387 ->disableOriginalConstructor()
388 ->getMock();
389 $backend->expects($this->once())->method('isCacheFileExpired')->with('vfs://Foo/cache/data/UnitTestCache/ExpiredEntry')->will($this->returnValue(true));
390 $backend->setCacheDirectory('vfs://Foo/');
391 $backend->setCache($mockCache);
392
393 $this->assertFalse($backend->get('ExpiredEntry'));
394 }
395
396 /**
397 * @test
398 * @throws Exception
399 */
400 public function getDoesNotCheckIfAnEntryIsExpiredIfTheCacheIsFrozen(): void
401 {
402 $mockCache = $this->createMock(AbstractFrontend::class);
403 $mockCache->expects($this->atLeastOnce())->method('getIdentifier')->will($this->returnValue('UnitTestCache'));
404
405 $backend = $this->getMockBuilder(FileBackend::class)
406 ->setMethods(['isCacheFileExpired'])
407 ->disableOriginalConstructor()
408 ->getMock();
409 $backend->setCacheDirectory('vfs://Foo/');
410 $backend->setCache($mockCache);
411
412 $backend->expects($this->once())->method('isCacheFileExpired');
413
414 $backend->set('foo', 'some data');
415 $backend->freeze();
416 $this->assertEquals('some data', $backend->get('foo'));
417 $this->assertFalse($backend->get('bar'));
418 }
419
420 /**
421 * @test
422 * @throws Exception
423 */
424 public function hasReturnsTrueIfAnEntryExists(): void
425 {
426 $mockCache = $this->createMock(AbstractFrontend::class);
427 $mockCache->expects($this->atLeastOnce())->method('getIdentifier')->will($this->returnValue('UnitTestCache'));
428
429 $backend = $this->getMockBuilder(FileBackend::class)
430 ->setMethods(['dummy'])
431 ->disableOriginalConstructor()
432 ->getMock();
433 $backend->setCacheDirectory('vfs://Foo/');
434 $backend->setCache($mockCache);
435
436 $entryIdentifier = 'BackendFileTest';
437
438 $data = 'some data' . microtime();
439 $backend->set($entryIdentifier, $data);
440
441 $this->assertTrue($backend->has($entryIdentifier), 'has() did not return TRUE.');
442 $this->assertFalse($backend->has($entryIdentifier . 'Not'), 'has() did not return FALSE.');
443 }
444
445 /**
446 * @test
447 */
448 public function hasReturnsFalseForExpiredEntries(): void
449 {
450 $backend = $this->getMockBuilder(FileBackend::class)
451 ->setMethods(['isCacheFileExpired'])
452 ->disableOriginalConstructor()
453 ->getMock();
454 $backend->expects($this->exactly(2))->method('isCacheFileExpired')->will($this->onConsecutiveCalls(
455 true,
456 false
457 ));
458
459 $this->assertFalse($backend->has('foo'));
460 $this->assertTrue($backend->has('bar'));
461 }
462
463 /**
464 * @test
465 * @throws Exception
466 */
467 public function hasDoesNotCheckIfAnEntryIsExpiredIfTheCacheIsFrozen(): void
468 {
469 $mockCache = $this->createMock(AbstractFrontend::class);
470 $mockCache->expects($this->atLeastOnce())->method('getIdentifier')->will($this->returnValue('UnitTestCache'));
471
472 $backend = $this->getMockBuilder(FileBackend::class)
473 ->setMethods(['isCacheFileExpired'])
474 ->disableOriginalConstructor()
475 ->getMock();
476 $backend->setCacheDirectory('vfs://Foo/');
477 $backend->setCache($mockCache);
478
479 $backend->expects($this->once())->method('isCacheFileExpired'); // Indirectly called by freeze() -> get()
480
481 $backend->set('foo', 'some data');
482 $backend->freeze();
483 $this->assertTrue($backend->has('foo'));
484 $this->assertFalse($backend->has('bar'));
485 }
486
487 /**
488 * @test
489 * @throws Exception
490 */
491 public function removeReallyRemovesACacheEntry(): void
492 {
493 $mockCache = $this->createMock(AbstractFrontend::class);
494 $mockCache->expects($this->atLeastOnce())->method('getIdentifier')->will($this->returnValue('UnitTestCache'));
495
496 $data = 'some data' . microtime();
497 $entryIdentifier = 'BackendFileTest';
498 $pathAndFilename = 'vfs://Foo/cache/data/UnitTestCache/' . $entryIdentifier;
499
500 $backend = $this->getMockBuilder(FileBackend::class)
501 ->setMethods(['dummy'])
502 ->disableOriginalConstructor()
503 ->getMock();
504 $backend->setCacheDirectory('vfs://Foo/');
505 $backend->setCache($mockCache);
506
507 $backend->set($entryIdentifier, $data);
508 $this->assertFileExists($pathAndFilename);
509
510 $backend->remove($entryIdentifier);
511 $this->assertFileNotExists($pathAndFilename);
512 }
513
514 /**
515 */
516 public function invalidEntryIdentifiers(): array
517 {
518 return [
519 'trailing slash' => ['/myIdentifer'],
520 'trailing dot and slash' => ['./myIdentifer'],
521 'trailing two dots and slash' => ['../myIdentifier'],
522 'trailing with multiple dots and slashes' => ['.././../myIdentifier'],
523 'slash in middle part' => ['my/Identifier'],
524 'dot and slash in middle part' => ['my./Identifier'],
525 'two dots and slash in middle part' => ['my../Identifier'],
526 'multiple dots and slashes in middle part' => ['my.././../Identifier'],
527 'pending slash' => ['myIdentifier/'],
528 'pending dot and slash' => ['myIdentifier./'],
529 'pending dots and slash' => ['myIdentifier../'],
530 'pending multiple dots and slashes' => ['myIdentifier.././../'],
531 ];
532 }
533
534 /**
535 * @test
536 * @dataProvider invalidEntryIdentifiers
537 * @param string $identifier
538 * @throws Exception
539 * @throws InvalidDataException
540 */
541 public function setThrowsExceptionForInvalidIdentifier(string $identifier): void
542 {
543 $this->expectException(\InvalidArgumentException::class);
544 $this->expectExceptionCode(1282073032);
545
546 $mockCache = $this->createMock(AbstractFrontend::class);
547 $mockCache->expects($this->atLeastOnce())->method('getIdentifier')->will($this->returnValue('UnitTestCache'));
548
549 $backend = $this->getMockBuilder(FileBackend::class)
550 ->setMethods(['dummy'])
551 ->setConstructorArgs(['test'])
552 ->getMock();
553 $backend->setCacheDirectory('vfs://Foo/');
554 $backend->setCache($mockCache);
555
556 $backend->set($identifier, 'cache data', []);
557 }
558
559 /**
560 * @test
561 * @dataProvider invalidEntryIdentifiers
562 * @param string $identifier
563 * @throws Exception
564 */
565 public function getThrowsExceptionForInvalidIdentifier(string $identifier): void
566 {
567 $this->expectException(\InvalidArgumentException::class);
568 $this->expectExceptionCode(1282073033);
569
570 $mockCache = $this->createMock(AbstractFrontend::class);
571 $mockCache->expects($this->atLeastOnce())->method('getIdentifier')->will($this->returnValue('UnitTestCache'));
572
573 $backend = $this->getMockBuilder(FileBackend::class)
574 ->setMethods(['dummy'])
575 ->disableOriginalConstructor()
576 ->getMock();
577 $backend->setCacheDirectory('vfs://Foo/');
578 $backend->setCache($mockCache);
579
580 $backend->get($identifier);
581 }
582
583 /**
584 * @test
585 * @dataProvider invalidEntryIdentifiers
586 * @param string $identifier
587 */
588 public function hasThrowsExceptionForInvalidIdentifier(string $identifier): void
589 {
590 $this->expectException(\InvalidArgumentException::class);
591 $this->expectExceptionCode(1282073034);
592
593 $backend = $this->getMockBuilder(FileBackend::class)
594 ->setMethods(['dummy'])
595 ->disableOriginalConstructor()
596 ->getMock();
597
598 $backend->has($identifier);
599 }
600
601 /**
602 * @test
603 * @dataProvider invalidEntryIdentifiers
604 * @param string $identifier
605 * @throws Exception
606 */
607 public function removeThrowsExceptionForInvalidIdentifier(string $identifier): void
608 {
609 $this->expectException(\InvalidArgumentException::class);
610 $this->expectExceptionCode(1282073035);
611
612 $mockCache = $this->createMock(AbstractFrontend::class);
613 $mockCache->expects($this->atLeastOnce())->method('getIdentifier')->will($this->returnValue('UnitTestCache'));
614
615 $backend = $this->getMockBuilder(FileBackend::class)
616 ->setMethods(['dummy'])
617 ->disableOriginalConstructor()
618 ->getMock();
619 $backend->setCacheDirectory('vfs://Foo/');
620 $backend->setCache($mockCache);
621
622 $backend->remove($identifier);
623 }
624
625 /**
626 * @test
627 * @dataProvider invalidEntryIdentifiers
628 * @param string $identifier
629 * @throws Exception
630 */
631 public function requireOnceThrowsExceptionForInvalidIdentifier(string $identifier): void
632 {
633 $this->expectException(\InvalidArgumentException::class);
634 $this->expectExceptionCode(1282073036);
635
636 $mockCache = $this->createMock(AbstractFrontend::class);
637 $mockCache->expects($this->atLeastOnce())->method('getIdentifier')->will($this->returnValue('UnitTestCache'));
638
639 $backend = $this->getMockBuilder(FileBackend::class)
640 ->setMethods(['dummy'])
641 ->disableOriginalConstructor()
642 ->getMock();
643 $backend->setCacheDirectory('vfs://Foo/');
644 $backend->setCache($mockCache);
645
646 $backend->requireOnce($identifier);
647 }
648
649 /**
650 * @test
651 * @throws Exception
652 */
653 public function requireOnceIncludesAndReturnsResultOfIncludedPhpFile(): void
654 {
655 $mockCache = $this->createMock(AbstractFrontend::class);
656 $mockCache->expects($this->atLeastOnce())->method('getIdentifier')->will($this->returnValue('UnitTestCache'));
657
658 $backend = $this->getMockBuilder(FileBackend::class)
659 ->setMethods(['dummy'])
660 ->disableOriginalConstructor()
661 ->getMock();
662 $backend->setCacheDirectory('vfs://Foo/');
663 $backend->setCache($mockCache);
664
665 $entryIdentifier = 'SomePhpEntry';
666
667 $data = '<?php return "foo"; ?>';
668 $backend->set($entryIdentifier, $data);
669
670 $loadedData = $backend->requireOnce($entryIdentifier);
671 $this->assertEquals('foo', $loadedData);
672 }
673
674 /**
675 * @test
676 * @throws Exception
677 */
678 public function requireOnceDoesNotCheckExpiryTimeIfBackendIsFrozen(): void
679 {
680 $mockCache = $this->createMock(AbstractFrontend::class);
681 $mockCache->expects($this->atLeastOnce())->method('getIdentifier')->will($this->returnValue('UnitTestCache'));
682
683 $backend = $this->getMockBuilder(FileBackend::class)
684 ->setMethods(['isCacheFileExpired'])
685 ->disableOriginalConstructor()
686 ->getMock();
687 $backend->setCacheDirectory('vfs://Foo/');
688 $backend->setCache($mockCache);
689
690 $backend->expects($this->once())->method('isCacheFileExpired'); // Indirectly called by freeze() -> get()
691
692 $data = '<?php return "foo"; ?>';
693 $backend->set('FooEntry', $data);
694
695 $backend->freeze();
696
697 $loadedData = $backend->requireOnce('FooEntry');
698 $this->assertEquals('foo', $loadedData);
699 }
700
701 /**
702 * @test
703 * @throws Exception
704 */
705 public function findIdentifiersByTagFindsCacheEntriesWithSpecifiedTag(): void
706 {
707 $mockCache = $this->createMock(AbstractFrontend::class);
708 $mockCache->expects($this->atLeastOnce())->method('getIdentifier')->will($this->returnValue('UnitTestCache'));
709
710 $backend = $this->getMockBuilder(FileBackend::class)
711 ->setMethods(['dummy'])
712 ->disableOriginalConstructor()
713 ->getMock();
714 $backend->setCacheDirectory('vfs://Foo/');
715 $backend->setCache($mockCache);
716
717 $data = 'some data' . microtime();
718 $backend->set('BackendFileTest1', $data, ['UnitTestTag%test', 'UnitTestTag%boring']);
719 $backend->set('BackendFileTest2', $data, ['UnitTestTag%test', 'UnitTestTag%special']);
720 $backend->set('BackendFileTest3', $data, ['UnitTestTag%test']);
721
722 $expectedEntry = 'BackendFileTest2';
723
724 $actualEntries = $backend->findIdentifiersByTag('UnitTestTag%special');
725 $this->assertInternalType('array', $actualEntries);
726 $this->assertEquals($expectedEntry, array_pop($actualEntries));
727 }
728
729 /**
730 * @test
731 * @throws Exception
732 */
733 public function findIdentifiersByTagDoesNotReturnExpiredEntries(): void
734 {
735 $mockCache = $this->createMock(AbstractFrontend::class);
736 $mockCache->expects($this->atLeastOnce())->method('getIdentifier')->will($this->returnValue('UnitTestCache'));
737
738 $backend = $this->getMockBuilder(FileBackend::class)
739 ->setMethods(['dummy'])
740 ->disableOriginalConstructor()
741 ->getMock();
742 $backend->setCacheDirectory('vfs://Foo/');
743 $backend->setCache($mockCache);
744
745 $data = 'some data';
746 $backend->set('BackendFileTest1', $data, ['UnitTestTag%test', 'UnitTestTag%boring']);
747 $backend->set('BackendFileTest2', $data, ['UnitTestTag%test', 'UnitTestTag%special'], -100);
748 $backend->set('BackendFileTest3', $data, ['UnitTestTag%test']);
749
750 $this->assertSame([], $backend->findIdentifiersByTag('UnitTestTag%special'));
751 $this->assertSame(['BackendFileTest1', 'BackendFileTest3'], $backend->findIdentifiersByTag('UnitTestTag%test'));
752 }
753
754 /**
755 * @test
756 * @throws Exception
757 */
758 public function flushRemovesAllCacheEntries(): void
759 {
760 $mockCache = $this->createMock(AbstractFrontend::class);
761 $mockCache->expects($this->atLeastOnce())->method('getIdentifier')->will($this->returnValue('UnitTestCache'));
762
763 $backend = $this->getMockBuilder(FileBackend::class)
764 ->setMethods(['dummy'])
765 ->disableOriginalConstructor()
766 ->getMock();
767 $backend->setCacheDirectory('vfs://Foo/');
768 $backend->setCache($mockCache);
769
770 $data = 'some data';
771 $backend->set('BackendFileTest1', $data);
772 $backend->set('BackendFileTest2', $data);
773
774 $this->assertFileExists('vfs://Foo/cache/data/UnitTestCache/BackendFileTest1');
775 $this->assertFileExists('vfs://Foo/cache/data/UnitTestCache/BackendFileTest2');
776
777 $backend->flush();
778
779 $this->assertFileNotExists('vfs://Foo/cache/data/UnitTestCache/BackendFileTest1');
780 $this->assertFileNotExists('vfs://Foo/cache/data/UnitTestCache/BackendFileTest2');
781 }
782
783 /**
784 * @test
785 * @throws Exception
786 */
787 public function flushCreatesCacheDirectoryAgain(): void
788 {
789 $mockCache = $this->createMock(AbstractFrontend::class);
790 $mockCache->expects($this->atLeastOnce())->method('getIdentifier')->will($this->returnValue('UnitTestCache'));
791
792 $backend = $this->getMockBuilder(FileBackend::class)
793 ->setMethods(['dummy'])
794 ->disableOriginalConstructor()
795 ->getMock();
796 $backend->setCacheDirectory('vfs://Foo/');
797 $backend->setCache($mockCache);
798
799 $backend->flush();
800 $this->assertFileExists('vfs://Foo/cache/data/UnitTestCache/');
801 }
802
803 /**
804 * @test
805 */
806 public function flushByTagRemovesCacheEntriesWithSpecifiedTag(): void
807 {
808 $backend = $this->getMockBuilder(FileBackend::class)
809 ->setMethods(['findIdentifiersByTag', 'remove'])
810 ->disableOriginalConstructor()
811 ->getMock();
812
813 $backend->expects($this->once())->method('findIdentifiersByTag')->with('UnitTestTag%special')->will($this->returnValue([
814 'foo',
815 'bar',
816 'baz'
817 ]));
818 $backend->expects($this->at(1))->method('remove')->with('foo');
819 $backend->expects($this->at(2))->method('remove')->with('bar');
820 $backend->expects($this->at(3))->method('remove')->with('baz');
821
822 $backend->flushByTag('UnitTestTag%special');
823 }
824
825 /**
826 * @test
827 * @throws Exception
828 */
829 public function collectGarbageRemovesExpiredCacheEntries(): void
830 {
831 $mockCache = $this->createMock(AbstractFrontend::class);
832 $mockCache->expects($this->atLeastOnce())->method('getIdentifier')->will($this->returnValue('UnitTestCache'));
833
834 $backend = $this->getMockBuilder(FileBackend::class)
835 ->setMethods(['isCacheFileExpired'])
836 ->disableOriginalConstructor()
837 ->getMock();
838 $backend->expects($this->exactly(2))->method('isCacheFileExpired')->will($this->onConsecutiveCalls(
839 true,
840 false
841 ));
842 $backend->setCacheDirectory('vfs://Foo/');
843 $backend->setCache($mockCache);
844
845 $data = 'some data';
846 $backend->set('BackendFileTest1', $data);
847 $backend->set('BackendFileTest2', $data);
848
849 $this->assertFileExists('vfs://Foo/cache/data/UnitTestCache/BackendFileTest1');
850 $this->assertFileExists('vfs://Foo/cache/data/UnitTestCache/BackendFileTest2');
851
852 $backend->collectGarbage();
853 $this->assertFileNotExists('vfs://Foo/cache/data/UnitTestCache/BackendFileTest1');
854 $this->assertFileExists('vfs://Foo/cache/data/UnitTestCache/BackendFileTest2');
855 }
856
857 /**
858 * @test
859 * @throws Exception
860 */
861 public function flushUnfreezesTheCache(): void
862 {
863 $mockCache = $this->createMock(AbstractFrontend::class);
864 $mockCache->expects($this->atLeastOnce())->method('getIdentifier')->will($this->returnValue('UnitTestCache'));
865
866 $backend = $this->getMockBuilder(FileBackend::class)
867 ->setMethods(['dummy'])
868 ->disableOriginalConstructor()
869 ->getMock();
870 $backend->setCacheDirectory('vfs://Foo/');
871 $backend->setCache($mockCache);
872
873 $backend->freeze();
874
875 $this->assertTrue($backend->isFrozen());
876 $backend->flush();
877 $this->assertFalse($backend->isFrozen());
878 }
879 }