2fa908a724c8be4509057f9bbb16a925028ab20d
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Unit / Cache / Backend / MemcachedBackendTest.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\MemcachedBackend;
18
19 /**
20 * Testcase for the cache to memcached backend
21 *
22 * This file is a backport from FLOW3
23 */
24 class MemcachedBackendTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
25 {
26 /**
27 * Sets up this testcase
28 */
29 protected function setUp()
30 {
31 if (!extension_loaded('memcache') && !extension_loaded('memcached')) {
32 $this->markTestSkipped('Neither "memcache" nor "memcached" extension was available');
33 }
34 try {
35 if (!@fsockopen('localhost', 11211)) {
36 $this->markTestSkipped('memcached not reachable');
37 }
38 } catch (\Exception $e) {
39 $this->markTestSkipped('memcached not reachable');
40 }
41 }
42
43 /**
44 * @test
45 */
46 public function setThrowsExceptionIfNoFrontEndHasBeenSet()
47 {
48 $this->expectException(\TYPO3\CMS\Core\Cache\Exception::class);
49 $this->expectExceptionCode(1207149215);
50
51 $backendOptions = ['servers' => ['localhost:11211']];
52 $backend = new MemcachedBackend('Testing', $backendOptions);
53 $backend->initializeObject();
54 $data = 'Some data';
55 $identifier = $this->getUniqueId('MyIdentifier');
56 $backend->set($identifier, $data);
57 }
58
59 /**
60 * @test
61 */
62 public function initializeObjectThrowsExceptionIfNoMemcacheServerIsConfigured()
63 {
64 $this->expectException(\TYPO3\CMS\Core\Cache\Exception::class);
65 $this->expectExceptionCode(1213115903);
66
67 $backend = new MemcachedBackend('Testing');
68 $backend->initializeObject();
69 }
70
71 /**
72 * @test
73 */
74 public function itIsPossibleToSetAndCheckExistenceInCache()
75 {
76 $backend = $this->setUpBackend();
77 $data = 'Some data';
78 $identifier = $this->getUniqueId('MyIdentifier');
79 $backend->set($identifier, $data);
80 $inCache = $backend->has($identifier);
81 $this->assertTrue($inCache, 'Memcache failed to set and check entry');
82 }
83
84 /**
85 * @test
86 */
87 public function itIsPossibleToSetAndGetEntry()
88 {
89 $backend = $this->setUpBackend();
90 $data = 'Some data';
91 $identifier = $this->getUniqueId('MyIdentifier');
92 $backend->set($identifier, $data);
93 $fetchedData = $backend->get($identifier);
94 $this->assertEquals($data, $fetchedData, 'Memcache failed to set and retrieve data');
95 }
96
97 /**
98 * @test
99 */
100 public function itIsPossibleToRemoveEntryFromCache()
101 {
102 $backend = $this->setUpBackend();
103 $data = 'Some data';
104 $identifier = $this->getUniqueId('MyIdentifier');
105 $backend->set($identifier, $data);
106 $backend->remove($identifier);
107 $inCache = $backend->has($identifier);
108 $this->assertFalse($inCache, 'Failed to set and remove data from Memcache');
109 }
110
111 /**
112 * @test
113 */
114 public function itIsPossibleToOverwriteAnEntryInTheCache()
115 {
116 $backend = $this->setUpBackend();
117 $data = 'Some data';
118 $identifier = $this->getUniqueId('MyIdentifier');
119 $backend->set($identifier, $data);
120 $otherData = 'some other data';
121 $backend->set($identifier, $otherData);
122 $fetchedData = $backend->get($identifier);
123 $this->assertEquals($otherData, $fetchedData, 'Memcache failed to overwrite and retrieve data');
124 }
125
126 /**
127 * @test
128 */
129 public function findIdentifiersByTagFindsCacheEntriesWithSpecifiedTag()
130 {
131 $backend = $this->setUpBackend();
132 $data = 'Some data';
133 $identifier = $this->getUniqueId('MyIdentifier');
134 $backend->set($identifier, $data, ['UnitTestTag%tag1', 'UnitTestTag%tag2']);
135 $retrieved = $backend->findIdentifiersByTag('UnitTestTag%tag1');
136 $this->assertEquals($identifier, $retrieved[0], 'Could not retrieve expected entry by tag.');
137 $retrieved = $backend->findIdentifiersByTag('UnitTestTag%tag2');
138 $this->assertEquals($identifier, $retrieved[0], 'Could not retrieve expected entry by tag.');
139 }
140
141 /**
142 * @test
143 */
144 public function setRemovesTagsFromPreviousSet()
145 {
146 $backend = $this->setUpBackend();
147 $data = 'Some data';
148 $identifier = $this->getUniqueId('MyIdentifier');
149 $backend->set($identifier, $data, ['UnitTestTag%tag1', 'UnitTestTag%tag2']);
150 $backend->set($identifier, $data, ['UnitTestTag%tag3']);
151 $retrieved = $backend->findIdentifiersByTag('UnitTestTag%tagX');
152 $this->assertEquals([], $retrieved, 'Found entry which should no longer exist.');
153 }
154
155 /**
156 * @test
157 */
158 public function hasReturnsFalseIfTheEntryDoesntExist()
159 {
160 $backend = $this->setUpBackend();
161 $identifier = $this->getUniqueId('NonExistingIdentifier');
162 $inCache = $backend->has($identifier);
163 $this->assertFalse($inCache, '"has" did not return FALSE when checking on non existing identifier');
164 }
165
166 /**
167 * @test
168 */
169 public function removeReturnsFalseIfTheEntryDoesntExist()
170 {
171 $backend = $this->setUpBackend();
172 $identifier = $this->getUniqueId('NonExistingIdentifier');
173 $inCache = $backend->remove($identifier);
174 $this->assertFalse($inCache, '"remove" did not return FALSE when checking on non existing identifier');
175 }
176
177 /**
178 * @test
179 */
180 public function flushByTagRemovesCacheEntriesWithSpecifiedTag()
181 {
182 $backend = $this->setUpBackend();
183 $data = 'some data' . microtime();
184 $backend->set('BackendMemcacheTest1', $data, ['UnitTestTag%test', 'UnitTestTag%boring']);
185 $backend->set('BackendMemcacheTest2', $data, ['UnitTestTag%test', 'UnitTestTag%special']);
186 $backend->set('BackendMemcacheTest3', $data, ['UnitTestTag%test']);
187 $backend->flushByTag('UnitTestTag%special');
188 $this->assertTrue($backend->has('BackendMemcacheTest1'), 'BackendMemcacheTest1');
189 $this->assertFalse($backend->has('BackendMemcacheTest2'), 'BackendMemcacheTest2');
190 $this->assertTrue($backend->has('BackendMemcacheTest3'), 'BackendMemcacheTest3');
191 }
192
193 /**
194 * @test
195 */
196 public function flushByTagsRemovesCacheEntriesWithSpecifiedTags()
197 {
198 $backend = $this->setUpBackend();
199 $data = 'some data' . microtime();
200 $backend->set('BackendMemcacheTest1', $data, ['UnitTestTag%test', 'UnitTestTag%boring']);
201 $backend->set('BackendMemcacheTest2', $data, ['UnitTestTag%test', 'UnitTestTag%special']);
202 $backend->set('BackendMemcacheTest3', $data, ['UnitTestTag%test']);
203 $backend->flushByTags(['UnitTestTag%special', 'UnitTestTag%boring']);
204 $this->assertFalse($backend->has('BackendMemcacheTest1'), 'BackendMemcacheTest1');
205 $this->assertFalse($backend->has('BackendMemcacheTest2'), 'BackendMemcacheTest2');
206 $this->assertTrue($backend->has('BackendMemcacheTest3'), 'BackendMemcacheTest3');
207 }
208
209 /**
210 * @test
211 */
212 public function flushRemovesAllCacheEntries()
213 {
214 $backend = $this->setUpBackend();
215 $data = 'some data' . microtime();
216 $backend->set('BackendMemcacheTest1', $data);
217 $backend->set('BackendMemcacheTest2', $data);
218 $backend->set('BackendMemcacheTest3', $data);
219 $backend->flush();
220 $this->assertFalse($backend->has('BackendMemcacheTest1'), 'BackendMemcacheTest1');
221 $this->assertFalse($backend->has('BackendMemcacheTest2'), 'BackendMemcacheTest2');
222 $this->assertFalse($backend->has('BackendMemcacheTest3'), 'BackendMemcacheTest3');
223 }
224
225 /**
226 * @test
227 */
228 public function flushRemovesOnlyOwnEntries()
229 {
230 $backendOptions = ['servers' => ['localhost:11211']];
231 /** @var \PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Cache\Frontend\FrontendInterface $thisCache */
232 $thisCache = $this->createMock(\TYPO3\CMS\Core\Cache\Frontend\AbstractFrontend::class);
233 $thisCache->expects($this->any())->method('getIdentifier')->will($this->returnValue('thisCache'));
234 $thisBackend = new MemcachedBackend('Testing', $backendOptions);
235 $thisBackend->setCache($thisCache);
236 $thisBackend->initializeObject();
237
238 /** @var \PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Cache\Frontend\FrontendInterface $thatCache */
239 $thatCache = $this->createMock(\TYPO3\CMS\Core\Cache\Frontend\AbstractFrontend::class);
240 $thatCache->expects($this->any())->method('getIdentifier')->will($this->returnValue('thatCache'));
241 $thatBackend = new MemcachedBackend('Testing', $backendOptions);
242 $thatBackend->setCache($thatCache);
243 $thatBackend->initializeObject();
244 $thisBackend->set('thisEntry', 'Hello');
245 $thatBackend->set('thatEntry', 'World!');
246 $thatBackend->flush();
247 $this->assertEquals('Hello', $thisBackend->get('thisEntry'));
248 $this->assertFalse($thatBackend->has('thatEntry'));
249 }
250
251 /**
252 * Check if we can store ~5 MB of data, this gives some headroom for the
253 * reflection data.
254 *
255 * @test
256 */
257 public function largeDataIsStored()
258 {
259 $backend = $this->setUpBackend();
260 $data = str_repeat('abcde', 1024 * 1024);
261 $backend->set('tooLargeData', $data);
262 $this->assertTrue($backend->has('tooLargeData'));
263 $this->assertEquals($backend->get('tooLargeData'), $data);
264 }
265
266 /**
267 * @test
268 */
269 public function setTagsOnlyOnceToIdentifier()
270 {
271 $backendOptions = ['servers' => ['localhost:11211']];
272 $identifier = $this->getUniqueId('MyIdentifier');
273 $tags = ['UnitTestTag%test', 'UnitTestTag%boring'];
274
275 $backend = $this->setUpBackend($backendOptions, true);
276 $backend->_call('addIdentifierToTags', $identifier, $tags);
277 $this->assertSame(
278 $tags,
279 $backend->_call('findTagsByIdentifier', $identifier)
280 );
281
282 $backend->_call('addIdentifierToTags', $identifier, $tags);
283 $this->assertSame(
284 $tags,
285 $backend->_call('findTagsByIdentifier', $identifier)
286 );
287 }
288
289 /**
290 * Sets up the memcached backend used for testing
291 *
292 * @param array $backendOptions Options for the memcache backend
293 * @param bool $accessible TRUE if backend should be encapsulated in accessible proxy otherwise FALSE.
294 * @return \TYPO3\TestingFramework\Core\AccessibleObjectInterface|MemcachedBackend
295 */
296 protected function setUpBackend(array $backendOptions = [], $accessible = false)
297 {
298 /** @var \PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Cache\Frontend\FrontendInterface $cache */
299 $cache = $this->createMock(\TYPO3\CMS\Core\Cache\Frontend\FrontendInterface::class);
300 if ($backendOptions == []) {
301 $backendOptions = ['servers' => ['localhost:11211']];
302 }
303 if ($accessible) {
304 $accessibleClassName = $this->buildAccessibleProxy(MemcachedBackend::class);
305 $backend = new $accessibleClassName('Testing', $backendOptions);
306 } else {
307 $backend = new MemcachedBackend('Testing', $backendOptions);
308 }
309 $backend->setCache($cache);
310 $backend->initializeObject();
311 return $backend;
312 }
313 }