1dc21226ac23680ba9d3a98df18f2281119e6c11
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Unit / Cache / Backend / ApcBackendTest.php
1 <?php
2 namespace TYPO3\CMS\Core\Tests\Unit\Cache\Backend;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Cache\Backend\ApcBackend;
18
19 /**
20 * Testcase for the APC cache backend.
21 *
22 * NOTE: If you want to execute these tests you need to enable apc in
23 * cli context (apc.enable_cli = 1)
24 */
25 class ApcBackendTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
26 {
27 /**
28 * Sets up this testcase
29 */
30 protected function setUp()
31 {
32 // Currently APCu identifies itself both as "apcu" and "apc" (for compatibility) although it doesn't provide the APC-opcache functionality
33 if (!extension_loaded('apc') || ini_get('apc.enabled') == 0 || ini_get('apc.enable_cli') == 0) {
34 $this->markTestSkipped('APC/APCu extension was not available, or it was disabled for CLI.');
35 }
36 if (ini_get('apc.slam_defense') == 1) {
37 $this->markTestSkipped('This testcase can only be executed with apc.slam_defense = Off');
38 }
39 }
40
41 /**
42 * @test
43 */
44 public function setThrowsExceptionIfNoFrontEndHasBeenSet()
45 {
46 $backend = new ApcBackend('Testing');
47 $data = 'Some data';
48 $identifier = $this->getUniqueId('MyIdentifier');
49 $this->expectException(\TYPO3\CMS\Core\Cache\Exception::class);
50 $this->expectExceptionCode(1232986818);
51 $backend->set($identifier, $data);
52 }
53
54 /**
55 * @test
56 */
57 public function itIsPossibleToSetAndCheckExistenceInCache()
58 {
59 $backend = $this->setUpBackend();
60 $data = 'Some data';
61 $identifier = $this->getUniqueId('MyIdentifier');
62 $backend->set($identifier, $data);
63 $inCache = $backend->has($identifier);
64 $this->assertTrue($inCache, 'APC backend failed to set and check entry');
65 }
66
67 /**
68 * @test
69 */
70 public function itIsPossibleToSetAndGetEntry()
71 {
72 $backend = $this->setUpBackend();
73 $data = 'Some data';
74 $identifier = $this->getUniqueId('MyIdentifier');
75 $backend->set($identifier, $data);
76 $fetchedData = $backend->get($identifier);
77 $this->assertEquals($data, $fetchedData, 'APC backend failed to set and retrieve data');
78 }
79
80 /**
81 * @test
82 */
83 public function itIsPossibleToRemoveEntryFromCache()
84 {
85 $backend = $this->setUpBackend();
86 $data = 'Some data';
87 $identifier = $this->getUniqueId('MyIdentifier');
88 $backend->set($identifier, $data);
89 $backend->remove($identifier);
90 $inCache = $backend->has($identifier);
91 $this->assertFalse($inCache, 'Failed to set and remove data from APC backend');
92 }
93
94 /**
95 * @test
96 */
97 public function itIsPossibleToOverwriteAnEntryInTheCache()
98 {
99 $backend = $this->setUpBackend();
100 $data = 'Some data';
101 $identifier = $this->getUniqueId('MyIdentifier');
102 $backend->set($identifier, $data);
103 $otherData = 'some other data';
104 $backend->set($identifier, $otherData);
105 $fetchedData = $backend->get($identifier);
106 $this->assertEquals($otherData, $fetchedData, 'APC backend failed to overwrite and retrieve data');
107 }
108
109 /**
110 * @test
111 */
112 public function findIdentifiersByTagFindsSetEntries()
113 {
114 $backend = $this->setUpBackend();
115 $data = 'Some data';
116 $identifier = $this->getUniqueId('MyIdentifier');
117 $backend->set($identifier, $data, ['UnitTestTag%tag1', 'UnitTestTag%tag2']);
118 $retrieved = $backend->findIdentifiersByTag('UnitTestTag%tag1');
119 $this->assertEquals($identifier, $retrieved[0], 'Could not retrieve expected entry by tag.');
120 $retrieved = $backend->findIdentifiersByTag('UnitTestTag%tag2');
121 $this->assertEquals($identifier, $retrieved[0], 'Could not retrieve expected entry by tag.');
122 }
123
124 /**
125 * @test
126 */
127 public function setRemovesTagsFromPreviousSet()
128 {
129 $backend = $this->setUpBackend();
130 $data = 'Some data';
131 $identifier = $this->getUniqueId('MyIdentifier');
132 $backend->set($identifier, $data, ['UnitTestTag%tag1', 'UnitTestTag%tagX']);
133 $backend->set($identifier, $data, ['UnitTestTag%tag3']);
134 $retrieved = $backend->findIdentifiersByTag('UnitTestTag%tagX');
135 $this->assertEquals([], $retrieved, 'Found entry which should no longer exist.');
136 }
137
138 /**
139 * @test
140 */
141 public function setCacheIsSettingIdentifierPrefixWithCacheIdentifier()
142 {
143 /** @var \PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Cache\Frontend\FrontendInterface $cacheMock */
144 $cacheMock = $this->createMock(\TYPO3\CMS\Core\Cache\Frontend\FrontendInterface::class);
145 $cacheMock->expects($this->any())->method('getIdentifier')->will($this->returnValue(
146 'testidentifier'
147 ));
148
149 /** @var $backendMock \PHPUnit_Framework_MockObject_MockObject|ApcBackend */
150 $backendMock = $this->getMockBuilder(ApcBackend::class)
151 ->setMethods(['setIdentifierPrefix', 'getCurrentUserData', 'getPathSite'])
152 ->setConstructorArgs(['testcontext'])
153 ->getMock();
154
155 $backendMock->expects($this->once())->method('getCurrentUserData')->will(
156 $this->returnValue(['name' => 'testname'])
157 );
158
159 $backendMock->expects($this->once())->method('getPathSite')->will(
160 $this->returnValue('testpath')
161 );
162
163 $expectedIdentifier = 'TYPO3_' . \TYPO3\CMS\Core\Utility\GeneralUtility::shortMD5('testpath' . 'testname' . 'testcontext' . 'testidentifier', 12);
164 $backendMock->expects($this->once())->method('setIdentifierPrefix')->with($expectedIdentifier);
165 $backendMock->setCache($cacheMock);
166 }
167
168 /**
169 * @test
170 */
171 public function hasReturnsFalseIfTheEntryDoesNotExist()
172 {
173 $backend = $this->setUpBackend();
174 $identifier = $this->getUniqueId('NonExistingIdentifier');
175 $inCache = $backend->has($identifier);
176 $this->assertFalse($inCache, '"has" did not return FALSE when checking on non existing identifier');
177 }
178
179 /**
180 * @test
181 */
182 public function removeReturnsFalseIfTheEntryDoesntExist()
183 {
184 $backend = $this->setUpBackend();
185 $identifier = $this->getUniqueId('NonExistingIdentifier');
186 $inCache = $backend->remove($identifier);
187 $this->assertFalse($inCache, '"remove" did not return FALSE when checking on non existing identifier');
188 }
189
190 /**
191 * @test
192 */
193 public function flushByTagRemovesCacheEntriesWithSpecifiedTag()
194 {
195 $backend = $this->setUpBackend();
196 $data = 'some data' . microtime();
197 $backend->set('BackendAPCTest1', $data, ['UnitTestTag%test', 'UnitTestTag%boring']);
198 $backend->set('BackendAPCTest2', $data, ['UnitTestTag%test', 'UnitTestTag%special']);
199 $backend->set('BackendAPCTest3', $data, ['UnitTestTag%test']);
200 $backend->flushByTag('UnitTestTag%special');
201 $this->assertTrue($backend->has('BackendAPCTest1'), 'BackendAPCTest1');
202 $this->assertFalse($backend->has('BackendAPCTest2'), 'BackendAPCTest2');
203 $this->assertTrue($backend->has('BackendAPCTest3'), 'BackendAPCTest3');
204 }
205
206 /**
207 * @test
208 */
209 public function flushByTagsRemovesCacheEntriesWithSpecifiedTags()
210 {
211 $backend = $this->setUpBackend();
212 $data = 'some data' . microtime();
213 $backend->set('BackendAPCTest1', $data, ['UnitTestTag%test', 'UnitTestTag%boring']);
214 $backend->set('BackendAPCTest2', $data, ['UnitTestTag%test', 'UnitTestTag%special']);
215 $backend->set('BackendAPCTest3', $data, ['UnitTestTag%test']);
216 $backend->flushByTags(['UnitTestTag%special', 'UnitTestTag%boring']);
217 $this->assertFalse($backend->has('BackendAPCTest1'), 'BackendAPCTest1');
218 $this->assertFalse($backend->has('BackendAPCTest2'), 'BackendAPCTest2');
219 $this->assertTrue($backend->has('BackendAPCTest3'), 'BackendAPCTest3');
220 }
221
222 /**
223 * @test
224 */
225 public function flushRemovesAllCacheEntries()
226 {
227 $backend = $this->setUpBackend();
228 $data = 'some data' . microtime();
229 $backend->set('BackendAPCTest1', $data);
230 $backend->set('BackendAPCTest2', $data);
231 $backend->set('BackendAPCTest3', $data);
232 $backend->flush();
233 $this->assertFalse($backend->has('BackendAPCTest1'), 'BackendAPCTest1');
234 $this->assertFalse($backend->has('BackendAPCTest2'), 'BackendAPCTest2');
235 $this->assertFalse($backend->has('BackendAPCTest3'), 'BackendAPCTest3');
236 }
237
238 /**
239 * @test
240 */
241 public function flushRemovesOnlyOwnEntries()
242 {
243 /** @var \PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Cache\Frontend\FrontendInterface $thisCache */
244 $thisCache = $this->createMock(\TYPO3\CMS\Core\Cache\Frontend\FrontendInterface::class);
245 $thisCache->expects($this->any())->method('getIdentifier')->will($this->returnValue('thisCache'));
246 $thisBackend = new ApcBackend('Testing');
247 $thisBackend->setCache($thisCache);
248
249 /** @var \PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Cache\Frontend\FrontendInterface $thatCache */
250 $thatCache = $this->createMock(\TYPO3\CMS\Core\Cache\Frontend\FrontendInterface::class);
251 $thatCache->expects($this->any())->method('getIdentifier')->will($this->returnValue('thatCache'));
252 $thatBackend = new ApcBackend('Testing');
253 $thatBackend->setCache($thatCache);
254 $thisBackend->set('thisEntry', 'Hello');
255 $thatBackend->set('thatEntry', 'World!');
256 $thatBackend->flush();
257 $this->assertEquals('Hello', $thisBackend->get('thisEntry'));
258 $this->assertFalse($thatBackend->has('thatEntry'));
259 }
260
261 /**
262 * Check if we can store ~5 MB of data
263 *
264 * @test
265 */
266 public function largeDataIsStored()
267 {
268 $backend = $this->setUpBackend();
269 $data = str_repeat('abcde', 1024 * 1024);
270 $identifier = $this->getUniqueId('tooLargeData');
271 $backend->set($identifier, $data);
272 $this->assertTrue($backend->has($identifier));
273 $this->assertEquals($backend->get($identifier), $data);
274 }
275
276 /**
277 * @test
278 */
279 public function setTagsOnlyOnceToIdentifier()
280 {
281 $identifier = $this->getUniqueId('MyIdentifier');
282 $tags = ['UnitTestTag%test', 'UnitTestTag%boring'];
283
284 $backend = $this->setUpBackend(true);
285 $backend->_call('addIdentifierToTags', $identifier, $tags);
286 $this->assertSame(
287 $tags,
288 $backend->_call('findTagsByIdentifier', $identifier)
289 );
290
291 $backend->_call('addIdentifierToTags', $identifier, $tags);
292 $this->assertSame(
293 $tags,
294 $backend->_call('findTagsByIdentifier', $identifier)
295 );
296 }
297
298 /**
299 * Sets up the APC backend used for testing
300 *
301 * @param bool $accessible TRUE if backend should be encapsulated in accessible proxy otherwise FALSE.
302 * @return \TYPO3\TestingFramework\Core\AccessibleObjectInterface|ApcBackend
303 */
304 protected function setUpBackend($accessible = false)
305 {
306 /** @var \PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Cache\Frontend\FrontendInterface $cache */
307 $cache = $this->createMock(\TYPO3\CMS\Core\Cache\Frontend\FrontendInterface::class);
308 if ($accessible) {
309 $accessibleClassName = $this->buildAccessibleProxy(ApcBackend::class);
310 $backend = new $accessibleClassName('Testing');
311 } else {
312 $backend = new ApcBackend('Testing');
313 }
314 $backend->setCache($cache);
315 return $backend;
316 }
317 }