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