‪TYPO3CMS  ‪main
FileBackendTest.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use PHPUnit\Framework\Attributes\DataProvider;
21 use PHPUnit\Framework\Attributes\Test;
30 use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
31 
32 final class FileBackendTest extends FunctionalTestCase
33 {
34  protected bool $initializeDatabase = false;
35 
36  protected function tearDown(): void
37  {
38  ‪GeneralUtility::rmdir($this->instancePath . '/Foo/', true);
39  parent::tearDown();
40  }
41 
42  #[Test]
43  public function setCacheDirectoryThrowsExceptionOnNonWritableDirectory(): void
44  {
45  $this->expectException(Exception::class);
46  $this->expectExceptionCode(1303669848);
47  $subject = new FileBackend('');
48  $subject->setCacheDirectory('http://localhost/');
49  $subject->setCache(new NullFrontend('foo'));
50  }
51 
52  #[Test]
53  public function setCacheDirectoryAllowsAbsolutePathWithoutTrailingSlash(): void
54  {
55  $subject = $this->getAccessibleMock(FileBackend::class, null, [], '', false);
56  $subject->setCacheDirectory('/tmp/foo');
57  self::assertEquals('/tmp/foo/', $subject->_get('temporaryCacheDirectory'));
58  }
59 
60  #[Test]
61  public function setCacheDirectoryAllowsAbsolutePathWithTrailingSlash(): void
62  {
63  $subject = $this->getAccessibleMock(FileBackend::class, null, [], '', false);
64  $subject->setCacheDirectory('/tmp/foo/');
65  self::assertEquals('/tmp/foo/', $subject->_get('temporaryCacheDirectory'));
66  }
67 
68  #[Test]
69  public function setCacheDirectoryAllowsRelativePathWithoutTrailingSlash(): void
70  {
71  $subject = $this->getAccessibleMock(FileBackend::class, null, [], '', false);
72  $subject->setCacheDirectory('tmp/foo');
74  self::assertEquals($path . '/tmp/foo/', $subject->_get('temporaryCacheDirectory'));
75  }
76 
77  #[Test]
78  public function setCacheDirectoryAllowsRelativePathWithTrailingSlash(): void
79  {
80  $subject = $this->getAccessibleMock(FileBackend::class, null, [], '', false);
81  $subject->setCacheDirectory('tmp/foo/');
83  self::assertEquals($path . '/tmp/foo/', $subject->_get('temporaryCacheDirectory'));
84  }
85 
86  #[Test]
87  public function setCacheDirectoryAllowsRelativeDottedPathWithoutTrailingSlash(): void
88  {
89  $subject = $this->getAccessibleMock(FileBackend::class, null, [], '', false);
90  $subject->setCacheDirectory('../tmp/foo');
92  self::assertEquals($path . '/../tmp/foo/', $subject->_get('temporaryCacheDirectory'));
93  }
94 
95  #[Test]
96  public function setCacheDirectoryAllowsRelativeDottedPathWithTrailingSlash(): void
97  {
98  $subject = $this->getAccessibleMock(FileBackend::class, null, [], '', false);
99  $subject->setCacheDirectory('../tmp/foo/');
101  self::assertEquals($path . '/../tmp/foo/', $subject->_get('temporaryCacheDirectory'));
102  }
103 
104  #[Test]
105  public function setCacheDirectoryAllowsAbsoluteDottedPathWithoutTrailingSlash(): void
106  {
107  $subject = $this->getAccessibleMock(FileBackend::class, null, [], '', false);
108  $subject->setCacheDirectory('/tmp/../foo');
109  self::assertEquals('/tmp/../foo/', $subject->_get('temporaryCacheDirectory'));
110  }
111 
112  #[Test]
113  public function setCacheDirectoryAllowsAbsoluteDottedPathWithTrailingSlash(): void
114  {
115  $subject = $this->getAccessibleMock(FileBackend::class, null, [], '', false);
116  $subject->setCacheDirectory('/tmp/../foo/');
117  self::assertEquals('/tmp/../foo/', $subject->_get('temporaryCacheDirectory'));
118  }
119 
120  #[Test]
121  public function getCacheDirectoryReturnsTheCurrentCacheDirectory(): void
122  {
123  $subject = new FileBackend('');
124  $subject->setCacheDirectory($this->instancePath . '/Foo/');
125  $subject->setCache(new NullFrontend('SomeCache'));
126  self::assertEquals($this->instancePath . '/Foo/cache/code/SomeCache/', $subject->getCacheDirectory());
127  }
128 
129  #[Test]
130  public function aDedicatedCacheDirectoryIsUsedForCodeCaches(): void
131  {
132  $mockCache = $this->createMock(PhpFrontend::class);
133  $mockCache->method('getIdentifier')->willReturn('SomeCache');
134  $subject = new FileBackend('');
135  $subject->setCacheDirectory($this->instancePath . '/Foo/');
136  $subject->setCache($mockCache);
137  self::assertEquals($this->instancePath . '/Foo/cache/code/SomeCache/', $subject->getCacheDirectory());
138  }
139 
140  #[Test]
141  public function setThrowsExceptionIfDataIsNotAString(): void
142  {
143  $this->expectException(InvalidDataException::class);
144  $this->expectExceptionCode(1204481674);
145  $subject = new FileBackend('');
146  $subject->setCacheDirectory($this->instancePath . '/Foo/');
147  $subject->setCache(new NullFrontend('SomeCache'));
148  $subject->set('some identifier', ['not a string']);
149  }
150 
151  #[Test]
152  public function setReallySavesToTheSpecifiedDirectory(): void
153  {
154  $mockCache = $this->createMock(AbstractFrontend::class);
155  $mockCache->expects(self::atLeastOnce())->method('getIdentifier')->willReturn('UnitTestCache');
156  $data = 'some data' . microtime();
157  $entryIdentifier = 'BackendFileTest';
158  $pathAndFilename = $this->instancePath . '/Foo/cache/data/UnitTestCache/' . $entryIdentifier;
159  $subject = new FileBackend('');
160  $subject->setCacheDirectory($this->instancePath . '/Foo/');
161  $subject->setCache($mockCache);
162  $subject->set($entryIdentifier, $data);
163  self::assertFileExists($pathAndFilename);
164  $retrievedData = file_get_contents($pathAndFilename, false, null, 0, \strlen($data));
165  self::assertEquals($data, $retrievedData);
166  }
167 
168  #[Test]
169  public function setOverwritesAnAlreadyExistingCacheEntryForTheSameIdentifier(): void
170  {
171  $mockCache = $this->createMock(AbstractFrontend::class);
172  $mockCache->expects(self::atLeastOnce())->method('getIdentifier')->willReturn('UnitTestCache');
173  $data1 = 'some data' . microtime();
174  $data2 = 'some data' . microtime();
175  $entryIdentifier = 'BackendFileRemoveBeforeSetTest';
176  $subject = new FileBackend('');
177  $subject->setCacheDirectory($this->instancePath . '/Foo/');
178  $subject->setCache($mockCache);
179  $subject->set($entryIdentifier, $data1, [], 500);
180  $subject->set($entryIdentifier, $data2, [], 200);
181  $pathAndFilename = $this->instancePath . '/Foo/cache/data/UnitTestCache/' . $entryIdentifier;
182  self::assertFileExists($pathAndFilename);
183  $retrievedData = file_get_contents($pathAndFilename, false, null, 0, \strlen($data2));
184  self::assertEquals($data2, $retrievedData);
185  }
186 
187  #[Test]
188  public function setAlsoSavesSpecifiedTags(): void
189  {
190  $mockCache = $this->createMock(AbstractFrontend::class);
191  $mockCache->expects(self::atLeastOnce())->method('getIdentifier')->willReturn('UnitTestCache');
192  $data = 'some data' . microtime();
193  $entryIdentifier = 'BackendFileRemoveBeforeSetTest';
194  $subject = new FileBackend('');
195  $subject->setCacheDirectory($this->instancePath . '/Foo/');
196  $subject->setCache($mockCache);
197  $subject->set($entryIdentifier, $data, ['Tag1', 'Tag2']);
198  $pathAndFilename = $this->instancePath . '/Foo/cache/data/UnitTestCache/' . $entryIdentifier;
199  self::assertFileExists($pathAndFilename);
200  $retrievedData = file_get_contents($pathAndFilename, false, null, \strlen($data) + ‪FileBackend::EXPIRYTIME_LENGTH, 9);
201  self::assertEquals('Tag1 Tag2', $retrievedData);
202  }
203 
204  #[Test]
205  public function setCacheDetectsAndLoadsAFrozenCache(): void
206  {
207  $mockCache = $this->createMock(AbstractFrontend::class);
208  $mockCache->expects(self::atLeastOnce())->method('getIdentifier')->willReturn('UnitTestCache');
209  $data = 'some data' . microtime();
210  $entryIdentifier = 'BackendFileTest';
211  $subject = new FileBackend('');
212  $subject->setCacheDirectory($this->instancePath . '/Foo/');
213  $subject->setCache($mockCache);
214  $subject->set($entryIdentifier, $data, ['Tag1', 'Tag2']);
215  $subject->freeze();
216  $subject = new FileBackend('');
217  $subject->setCacheDirectory($this->instancePath . '/Foo/');
218  $subject->setCache($mockCache);
219  self::assertTrue($subject->isFrozen());
220  self::assertEquals($data, $subject->get($entryIdentifier));
221  }
222 
223  #[Test]
224  public function getReturnsContentOfTheCorrectCacheFile(): void
225  {
226  $mockCache = $this->createMock(AbstractFrontend::class);
227  $mockCache->expects(self::atLeastOnce())->method('getIdentifier')->willReturn('UnitTestCache');
228  $subject = new FileBackend('');
229  $subject->setCacheDirectory($this->instancePath . '/Foo/');
230  $subject->setCache($mockCache);
231  $entryIdentifier = 'BackendFileTest';
232  $data = 'some data' . microtime();
233  $subject->set($entryIdentifier, $data, [], 500);
234  $data = 'some other data' . microtime();
235  $subject->set($entryIdentifier, $data, [], 100);
236  $loadedData = $subject->get($entryIdentifier);
237  self::assertEquals($data, $loadedData);
238  }
239 
240  #[Test]
241  public function getReturnsFalseForExpiredEntries(): void
242  {
243  $mockCache = $this->createMock(AbstractFrontend::class);
244  $mockCache->expects(self::atLeastOnce())->method('getIdentifier')->willReturn('UnitTestCache');
245  $subject = $this->getMockBuilder(FileBackend::class)->onlyMethods(['isCacheFileExpired'])->disableOriginalConstructor()->getMock();
246  $subject->expects(self::once())->method('isCacheFileExpired')->with($this->instancePath . '/Foo/cache/data/UnitTestCache/ExpiredEntry')->willReturn(true);
247  $subject->setCacheDirectory($this->instancePath . '/Foo/');
248  $subject->setCache($mockCache);
249  self::assertFalse($subject->get('ExpiredEntry'));
250  }
251 
252  #[Test]
253  public function getDoesNotCheckIfAnEntryIsExpiredIfTheCacheIsFrozen(): void
254  {
255  $mockCache = $this->createMock(AbstractFrontend::class);
256  $mockCache->expects(self::atLeastOnce())->method('getIdentifier')->willReturn('UnitTestCache');
257  $subject = $this->getMockBuilder(FileBackend::class)->onlyMethods(['isCacheFileExpired'])->disableOriginalConstructor()->getMock();
258  $subject->setCacheDirectory($this->instancePath . '/Foo/');
259  $subject->setCache($mockCache);
260  $subject->expects(self::once())->method('isCacheFileExpired');
261  $subject->set('foo', 'some data');
262  $subject->freeze();
263  self::assertEquals('some data', $subject->get('foo'));
264  self::assertFalse($subject->get('bar'));
265  }
266 
267  #[Test]
268  public function hasReturnsTrueIfAnEntryExists(): void
269  {
270  $mockCache = $this->createMock(AbstractFrontend::class);
271  $mockCache->expects(self::atLeastOnce())->method('getIdentifier')->willReturn('UnitTestCache');
272  $subject = new FileBackend('');
273  $subject->setCacheDirectory($this->instancePath . '/Foo/');
274  $subject->setCache($mockCache);
275  $entryIdentifier = 'BackendFileTest';
276  $data = 'some data' . microtime();
277  $subject->set($entryIdentifier, $data);
278  self::assertTrue($subject->has($entryIdentifier), 'has() did not return TRUE.');
279  self::assertFalse($subject->has($entryIdentifier . 'Not'), 'has() did not return FALSE.');
280  }
281 
282  #[Test]
283  public function hasReturnsFalseForExpiredEntries(): void
284  {
285  $subject = $this->getMockBuilder(FileBackend::class)->onlyMethods(['isCacheFileExpired'])->disableOriginalConstructor()->getMock();
286  $subject->expects(self::exactly(2))->method('isCacheFileExpired')->willReturn(true, false);
287  self::assertFalse($subject->has('foo'));
288  self::assertTrue($subject->has('bar'));
289  }
290 
291  #[Test]
292  public function hasDoesNotCheckIfAnEntryIsExpiredIfTheCacheIsFrozen(): void
293  {
294  $mockCache = $this->createMock(AbstractFrontend::class);
295  $mockCache->expects(self::atLeastOnce())->method('getIdentifier')->willReturn('UnitTestCache');
296  $subject = $this->getMockBuilder(FileBackend::class)->onlyMethods(['isCacheFileExpired'])->disableOriginalConstructor()->getMock();
297  $subject->setCacheDirectory($this->instancePath . '/Foo/');
298  $subject->setCache($mockCache);
299  $subject->expects(self::once())->method('isCacheFileExpired'); // Indirectly called by freeze() -> get()
300  $subject->set('foo', 'some data');
301  $subject->freeze();
302  self::assertTrue($subject->has('foo'));
303  self::assertFalse($subject->has('bar'));
304  }
305 
306  #[Test]
307  public function removeReallyRemovesACacheEntry(): void
308  {
309  $mockCache = $this->createMock(AbstractFrontend::class);
310  $mockCache->expects(self::atLeastOnce())->method('getIdentifier')->willReturn('UnitTestCache');
311  $data = 'some data' . microtime();
312  $entryIdentifier = 'BackendFileTest';
313  $pathAndFilename = $this->instancePath . '/Foo/cache/data/UnitTestCache/' . $entryIdentifier;
314  $subject = new FileBackend('');
315  $subject->setCacheDirectory($this->instancePath . '/Foo/');
316  $subject->setCache($mockCache);
317  $subject->set($entryIdentifier, $data);
318  self::assertFileExists($pathAndFilename);
319  $subject->remove($entryIdentifier);
320  self::assertFileDoesNotExist($pathAndFilename);
321  }
322 
323  public static function invalidEntryIdentifiers(): array
324  {
325  return [
326  'trailing slash' => ['/myIdentifier'],
327  'trailing dot and slash' => ['./myIdentifier'],
328  'trailing two dots and slash' => ['../myIdentifier'],
329  'trailing with multiple dots and slashes' => ['.././../myIdentifier'],
330  'slash in middle part' => ['my/Identifier'],
331  'dot and slash in middle part' => ['my./Identifier'],
332  'two dots and slash in middle part' => ['my../Identifier'],
333  'multiple dots and slashes in middle part' => ['my.././../Identifier'],
334  'pending slash' => ['myIdentifier/'],
335  'pending dot and slash' => ['myIdentifier./'],
336  'pending dots and slash' => ['myIdentifier../'],
337  'pending multiple dots and slashes' => ['myIdentifier.././../'],
338  ];
339  }
340 
341  #[DataProvider('invalidEntryIdentifiers')]
342  #[Test]
343  public function setThrowsExceptionForInvalidIdentifier(string ‪$identifier): void
344  {
345  $this->expectException(\InvalidArgumentException::class);
346  $this->expectExceptionCode(1282073032);
347  $mockCache = $this->createMock(AbstractFrontend::class);
348  $mockCache->expects(self::atLeastOnce())->method('getIdentifier')->willReturn('UnitTestCache');
349  $subject = new FileBackend('');
350  $subject->setCacheDirectory($this->instancePath . '/Foo/');
351  $subject->setCache($mockCache);
352  $subject->set(‪$identifier, 'cache data', []);
353  }
354 
355  #[DataProvider('invalidEntryIdentifiers')]
356  #[Test]
357  public function getThrowsExceptionForInvalidIdentifier(string ‪$identifier): void
358  {
359  $this->expectException(\InvalidArgumentException::class);
360  $this->expectExceptionCode(1282073033);
361  $mockCache = $this->createMock(AbstractFrontend::class);
362  $mockCache->expects(self::atLeastOnce())->method('getIdentifier')->willReturn('UnitTestCache');
363  $subject = new FileBackend('');
364  $subject->setCacheDirectory($this->instancePath . '/Foo/');
365  $subject->setCache($mockCache);
366  $subject->get(‪$identifier);
367  }
368 
369  #[DataProvider('invalidEntryIdentifiers')]
370  #[Test]
371  public function hasThrowsExceptionForInvalidIdentifier(string ‪$identifier): void
372  {
373  $this->expectException(\InvalidArgumentException::class);
374  $this->expectExceptionCode(1282073034);
375  $subject = new FileBackend('');
376  $subject->has(‪$identifier);
377  }
378 
379  #[DataProvider('invalidEntryIdentifiers')]
380  #[Test]
381  public function removeThrowsExceptionForInvalidIdentifier(string ‪$identifier): void
382  {
383  $this->expectException(\InvalidArgumentException::class);
384  $this->expectExceptionCode(1334756960);
385  $mockCache = $this->createMock(AbstractFrontend::class);
386  $mockCache->expects(self::atLeastOnce())->method('getIdentifier')->willReturn('UnitTestCache');
387  $subject = new FileBackend('');
388  $subject->setCacheDirectory($this->instancePath . '/Foo/');
389  $subject->setCache($mockCache);
390  $subject->remove(‪$identifier);
391  }
392 
393  #[DataProvider('invalidEntryIdentifiers')]
394  #[Test]
395  public function requireOnceThrowsExceptionForInvalidIdentifier(string ‪$identifier): void
396  {
397  $this->expectException(\InvalidArgumentException::class);
398  $this->expectExceptionCode(1282073036);
399  $mockCache = $this->createMock(AbstractFrontend::class);
400  $mockCache->expects(self::atLeastOnce())->method('getIdentifier')->willReturn('UnitTestCache');
401  $subject = new FileBackend('');
402  $subject->setCacheDirectory($this->instancePath . '/Foo/');
403  $subject->setCache($mockCache);
404  $subject->requireOnce(‪$identifier);
405  }
406 
407  #[Test]
408  public function requireOnceIncludesAndReturnsResultOfIncludedPhpFile(): void
409  {
410  $mockCache = $this->createMock(AbstractFrontend::class);
411  $mockCache->expects(self::atLeastOnce())->method('getIdentifier')->willReturn('UnitTestCache');
412  $subject = new FileBackend('');
413  $subject->setCacheDirectory($this->instancePath . '/Foo/');
414  $subject->setCache($mockCache);
415  $entryIdentifier = 'SomePhpEntry';
416  $data = '<?php return "foo"; ?>';
417  $subject->set($entryIdentifier, $data);
418  $loadedData = $subject->requireOnce($entryIdentifier);
419  self::assertEquals('foo', $loadedData);
420  }
421 
422  #[Test]
423  public function requireOnceDoesNotCheckExpiryTimeIfBackendIsFrozen(): void
424  {
425  $mockCache = $this->createMock(AbstractFrontend::class);
426  $mockCache->expects(self::atLeastOnce())->method('getIdentifier')->willReturn('UnitTestCache');
427  $subject = $this->getMockBuilder(FileBackend::class)->onlyMethods(['isCacheFileExpired'])->disableOriginalConstructor()->getMock();
428  $subject->setCacheDirectory($this->instancePath . '/Foo/');
429  $subject->setCache($mockCache);
430  $subject->expects(self::once())->method('isCacheFileExpired'); // Indirectly called by freeze() -> get()
431  $data = '<?php return "foo"; ?>';
432  $subject->set('FooEntry', $data);
433  $subject->freeze();
434  $loadedData = $subject->requireOnce('FooEntry');
435  self::assertEquals('foo', $loadedData);
436  }
437 
438  #[DataProvider('invalidEntryIdentifiers')]
439  #[Test]
440  public function requireThrowsExceptionForInvalidIdentifier(string ‪$identifier): void
441  {
442  $this->expectException(\InvalidArgumentException::class);
443  $this->expectExceptionCode(1532528246);
444  $mockCache = $this->createMock(AbstractFrontend::class);
445  $mockCache->expects(self::atLeastOnce())->method('getIdentifier')->willReturn('UnitTestCache');
446  $subject = new FileBackend('');
447  $subject->setCacheDirectory($this->instancePath . '/Foo/');
448  $subject->setCache($mockCache);
449  $subject->require(‪$identifier);
450  }
451 
452  #[Test]
453  public function requireIncludesAndReturnsResultOfIncludedPhpFile(): void
454  {
455  $mockCache = $this->createMock(AbstractFrontend::class);
456  $mockCache->expects(self::atLeastOnce())->method('getIdentifier')->willReturn('UnitTestCache');
457  $subject = new FileBackend('');
458  $subject->setCacheDirectory($this->instancePath . '/Foo/');
459  $subject->setCache($mockCache);
460  $entryIdentifier = 'SomePhpEntry2';
461  $data = '<?php return "foo2"; ?>';
462  $subject->set($entryIdentifier, $data);
463  $loadedData = $subject->require($entryIdentifier);
464  self::assertEquals('foo2', $loadedData);
465  }
466 
467  #[Test]
468  public function requireDoesNotCheckExpiryTimeIfBackendIsFrozen(): void
469  {
470  $mockCache = $this->createMock(AbstractFrontend::class);
471  $mockCache->expects(self::atLeastOnce())->method('getIdentifier')->willReturn('UnitTestCache');
472  $subject = $this->getMockBuilder(FileBackend::class)->onlyMethods(['isCacheFileExpired'])->disableOriginalConstructor()->getMock();
473  $subject->setCacheDirectory($this->instancePath . '/Foo/');
474  $subject->setCache($mockCache);
475  $subject->expects(self::once())->method('isCacheFileExpired'); // Indirectly called by freeze() -> get()
476  $data = '<?php return "foo"; ?>';
477  $subject->set('FooEntry2', $data);
478  $subject->freeze();
479  $loadedData = $subject->require('FooEntry2');
480  self::assertEquals('foo', $loadedData);
481  }
482 
483  #[Test]
484  public function requireCanLoadSameEntryMultipleTimes(): void
485  {
486  $frontendMock = $this->getMockBuilder(AbstractFrontend::class)->disableOriginalConstructor()->getMock();
487  $frontendMock->method('getIdentifier')->willReturn('UnitTestCache');
488  $subject = new FileBackend('Testing');
489  $subject->setCacheDirectory($this->instancePath . '/Foo/');
490  $subject->setCache($frontendMock);
491  $subject->set('BarEntry', '<?php return "foo"; ?>');
492  $loadedData = $subject->require('BarEntry');
493  self::assertEquals('foo', $loadedData);
494  $loadedData = $subject->require('BarEntry');
495  self::assertEquals('foo', $loadedData);
496  }
497 
498  #[Test]
499  public function findIdentifiersByTagFindsCacheEntriesWithSpecifiedTag(): void
500  {
501  $mockCache = $this->createMock(AbstractFrontend::class);
502  $mockCache->expects(self::atLeastOnce())->method('getIdentifier')->willReturn('UnitTestCache');
503  $subject = new FileBackend('');
504  $subject->setCacheDirectory($this->instancePath . '/Foo/');
505  $subject->setCache($mockCache);
506  $data = 'some data' . microtime();
507  $subject->set('BackendFileTest1', $data, ['UnitTestTag%test', 'UnitTestTag%boring']);
508  $subject->set('BackendFileTest2', $data, ['UnitTestTag%test', 'UnitTestTag%special']);
509  $subject->set('BackendFileTest3', $data, ['UnitTestTag%test']);
510  $expectedEntry = 'BackendFileTest2';
511  $actualEntries = $subject->findIdentifiersByTag('UnitTestTag%special');
512  self::assertIsArray($actualEntries);
513  self::assertEquals($expectedEntry, array_pop($actualEntries));
514  }
515 
516  #[Test]
517  public function findIdentifiersByTagDoesNotReturnExpiredEntries(): void
518  {
519  $mockCache = $this->createMock(AbstractFrontend::class);
520  $mockCache->expects(self::atLeastOnce())->method('getIdentifier')->willReturn('UnitTestCache');
521  $subject = new FileBackend('');
522  $subject->setCacheDirectory($this->instancePath . '/Foo/');
523  $subject->setCache($mockCache);
524  $data = 'some data';
525  $subject->set('BackendFileTest1', $data, ['UnitTestTag%test', 'UnitTestTag%boring']);
526  $subject->set('BackendFileTest2', $data, ['UnitTestTag%test', 'UnitTestTag%special'], -100);
527  $subject->set('BackendFileTest3', $data, ['UnitTestTag%test']);
528  self::assertSame([], $subject->findIdentifiersByTag('UnitTestTag%special'));
529  $actualEntries = $subject->findIdentifiersByTag('UnitTestTag%test');
530  self::assertContains('BackendFileTest1', $actualEntries);
531  self::assertContains('BackendFileTest3', $actualEntries);
532  }
533 
534  #[Test]
535  public function flushRemovesAllCacheEntries(): void
536  {
537  $mockCache = $this->createMock(AbstractFrontend::class);
538  $mockCache->expects(self::atLeastOnce())->method('getIdentifier')->willReturn('UnitTestCache');
539  $subject = new FileBackend('');
540  $subject->setCacheDirectory($this->instancePath . '/Foo/');
541  $subject->setCache($mockCache);
542  $data = 'some data';
543  $subject->set('BackendFileTest1', $data);
544  $subject->set('BackendFileTest2', $data);
545  self::assertFileExists($this->instancePath . '/Foo/cache/data/UnitTestCache/BackendFileTest1');
546  self::assertFileExists($this->instancePath . '/Foo/cache/data/UnitTestCache/BackendFileTest2');
547  $subject->flush();
548  self::assertFileDoesNotExist($this->instancePath . '/Foo/cache/data/UnitTestCache/BackendFileTest1');
549  self::assertFileDoesNotExist($this->instancePath . '/Foo/cache/data/UnitTestCache/BackendFileTest2');
550  }
551 
552  #[Test]
553  public function flushCreatesCacheDirectoryAgain(): void
554  {
555  $mockCache = $this->createMock(AbstractFrontend::class);
556  $mockCache->expects(self::atLeastOnce())->method('getIdentifier')->willReturn('UnitTestCache');
557  $subject = new FileBackend('');
558  $subject->setCacheDirectory($this->instancePath . '/Foo/');
559  $subject->setCache($mockCache);
560  $subject->flush();
561  self::assertFileExists($this->instancePath . '/Foo/cache/data/UnitTestCache/');
562  }
563 
564  #[Test]
565  public function flushByTagRemovesCacheEntriesWithSpecifiedTag(): void
566  {
567  $subject = $this->getMockBuilder(FileBackend::class)->onlyMethods(['findIdentifiersByTag', 'remove'])->disableOriginalConstructor()->getMock();
568  $subject->expects(self::once())->method('findIdentifiersByTag')->with('UnitTestTag%special')->willReturn([
569  'foo',
570  'bar',
571  'baz',
572  ]);
573  $series = [
574  ['foo'],
575  ['bar'],
576  ['baz'],
577  ];
578  $subject->expects(self::exactly(3))->method('remove')
579  ->willReturnCallback(function (string $value) use (&$series): void {
580  $arguments = array_shift($series);
581  self::assertSame($arguments[0], $value);
582  });
583  $subject->flushByTag('UnitTestTag%special');
584  }
585 
586  #[Test]
587  public function collectGarbageRemovesExpiredCacheEntries(): void
588  {
589  $mockCache = $this->createMock(AbstractFrontend::class);
590  $mockCache->expects(self::atLeastOnce())->method('getIdentifier')->willReturn('UnitTestCache');
591  $subject = $this->getMockBuilder(FileBackend::class)->onlyMethods(['isCacheFileExpired'])->disableOriginalConstructor()->getMock();
592  $subject->expects(self::exactly(2))->method('isCacheFileExpired')->willReturnMap([
593  [$this->instancePath . '/Foo/cache/data/UnitTestCache/BackendFileTest1', false],
594  [$this->instancePath . '/Foo/cache/data/UnitTestCache/BackendFileTest2', true],
595  ]);
596  $subject->setCacheDirectory($this->instancePath . '/Foo/');
597  $subject->setCache($mockCache);
598  $data = 'some data';
599  $subject->set('BackendFileTest1', $data);
600  $subject->set('BackendFileTest2', $data);
601  self::assertFileExists($this->instancePath . '/Foo/cache/data/UnitTestCache/BackendFileTest1');
602  self::assertFileExists($this->instancePath . '/Foo/cache/data/UnitTestCache/BackendFileTest2');
603  $subject->collectGarbage();
604  self::assertFileExists($this->instancePath . '/Foo/cache/data/UnitTestCache/BackendFileTest1');
605  self::assertFileDoesNotExist($this->instancePath . '/Foo/cache/data/UnitTestCache/BackendFileTest2');
606  }
607 
608  #[Test]
609  public function flushUnfreezesTheCache(): void
610  {
611  $mockCache = $this->createMock(AbstractFrontend::class);
612  $mockCache->expects(self::atLeastOnce())->method('getIdentifier')->willReturn('UnitTestCache');
613  $subject = new FileBackend('');
614  $subject->setCacheDirectory($this->instancePath . '/Foo/');
615  $subject->setCache($mockCache);
616  $subject->freeze();
617  self::assertTrue($subject->isFrozen());
618  $subject->flush();
619  self::assertFalse($subject->isFrozen());
620  }
621 }
‪TYPO3\CMS\Core\Tests\Functional\Cache\Backend
Definition: ApcuBackendTest.php:18
‪TYPO3\CMS\Core\Cache\Backend\FileBackend\EXPIRYTIME_LENGTH
‪const EXPIRYTIME_LENGTH
Definition: FileBackend.php:33
‪TYPO3\CMS\Core\Cache\Frontend\PhpFrontend
Definition: PhpFrontend.php:25
‪TYPO3\CMS\Core\Cache\Backend\FileBackend
Definition: FileBackend.php:30
‪TYPO3\CMS\Core\Cache\Frontend\NullFrontend
Definition: NullFrontend.php:30
‪TYPO3\CMS\Core\Core\Environment\getProjectPath
‪static string getProjectPath()
Definition: Environment.php:160
‪TYPO3\CMS\Core\Cache\Exception
Definition: DuplicateIdentifierException.php:16
‪TYPO3\CMS\Core\Cache\Exception\InvalidDataException
Definition: InvalidDataException.php:23
‪TYPO3\CMS\Core\Utility\GeneralUtility\rmdir
‪static bool rmdir(string $path, bool $removeNonEmpty=false)
Definition: GeneralUtility.php:1702
‪TYPO3\CMS\Core\Core\Environment
Definition: Environment.php:41
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Core\Cache\Frontend\AbstractFrontend
Definition: AbstractFrontend.php:25
‪TYPO3\CMS\Webhooks\Message\$identifier
‪identifier readonly string $identifier
Definition: FileAddedMessage.php:37