c85d12fec2160e833a355c0c95113c390b349b40
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Unit / Cache / Backend / RedisBackendTest.php
1 <?php
2 namespace TYPO3\CMS\Core\Tests\Unit\Cache\Backend;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 2010-2013 Christian Kuhn <lolli@schwarzbu.ch>
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 *
19 * This script is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * This copyright notice MUST APPEAR in all copies of the script!
25 ***************************************************************/
26
27 /**
28 * Testcase for the cache to redis backend
29 *
30 * This class has functional tests as well as implementation tests:
31 * - The functional tests make API calls to the backend and check expected behaviour
32 * - The implementation tests make additional calls with an own redis instance to
33 * check stored data structures in the redis server, which can not be checked
34 * by functional tests alone. Those tests will fail if any changes
35 * to the internal data structure are done.
36 *
37 * Warning:
38 * The unit tests use and flush redis database numbers 0 and 1!
39 *
40 * @author Christian Kuhn <lolli@schwarzbu.ch>
41 */
42 class RedisBackendTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
43
44 /**
45 * If set, the tearDown() method will flush the cache used by this unit test.
46 *
47 * @var \TYPO3\CMS\Core\Cache\Backend\RedisBackend
48 */
49 protected $backend = NULL;
50
51 /**
52 * Own redis instance used in implementation tests
53 *
54 * @var Redis
55 */
56 protected $redis = NULL;
57
58 /**
59 * Set up this testcase
60 */
61 public function setUp() {
62 if (!extension_loaded('redis')) {
63 $this->markTestSkipped('redis extension was not available');
64 }
65 try {
66 if (!@fsockopen('127.0.0.1', 6379)) {
67 $this->markTestSkipped('redis server not reachable');
68 }
69 } catch (\Exception $e) {
70 $this->markTestSkipped('redis server not reachable');
71 }
72 }
73
74 /**
75 * Sets up the redis backend used for testing
76 *
77 * @param array $backendOptions Options for the redis backend
78 */
79 protected function setUpBackend(array $backendOptions = array()) {
80 $mockCache = $this->getMock('TYPO3\\CMS\\Core\\Cache\\Frontend\\FrontendInterface', array(), array(), '', FALSE);
81 $mockCache->expects($this->any())->method('getIdentifier')->will($this->returnValue('TestCache'));
82 $this->backend = new \TYPO3\CMS\Core\Cache\Backend\RedisBackend('Testing', $backendOptions);
83 $this->backend->setCache($mockCache);
84 $this->backend->initializeObject();
85 }
86
87 /**
88 * Sets up an own redis instance for implementation tests
89 */
90 protected function setUpRedis() {
91 $this->redis = new \Redis();
92 $this->redis->connect('127.0.0.1', 6379);
93 }
94
95 /**
96 * Tear down this testcase
97 */
98 public function tearDown() {
99 if ($this->backend instanceof \TYPO3\CMS\Core\Cache\Backend\RedisBackend) {
100 $this->backend->flush();
101 }
102 parent::tearDown();
103 }
104
105 /**
106 * @test Functional
107 */
108 public function initializeObjectThrowsNoExceptionIfGivenDatabaseWasSuccessfullySelected() {
109 try {
110 $this->setUpBackend(array('database' => 1));
111 } catch (Exception $e) {
112 $this->assertTrue();
113 }
114 }
115
116 /**
117 * @test Functional
118 * @expectedException \InvalidArgumentException
119 */
120 public function setDatabaseThrowsExceptionIfGivenDatabaseNumberIsNotAnInteger() {
121 $this->setUpBackend(array('database' => 'foo'));
122 }
123
124 /**
125 * @test Functional
126 * @expectedException \InvalidArgumentException
127 */
128 public function setDatabaseThrowsExceptionIfGivenDatabaseNumberIsNegative() {
129 $this->setUpBackend(array('database' => -1));
130 }
131
132 /**
133 * @test Functional
134 * @expectedException \InvalidArgumentException
135 */
136 public function setCompressionThrowsExceptionIfCompressionParameterIsNotOfTypeBoolean() {
137 $this->setUpBackend(array('compression' => 'foo'));
138 }
139
140 /**
141 * @test Functional
142 * @expectedException \InvalidArgumentException
143 */
144 public function setCompressionLevelThrowsExceptionIfCompressionLevelIsNotInteger() {
145 $this->setUpBackend(array('compressionLevel' => 'foo'));
146 }
147
148 /**
149 * @test Functional
150 * @expectedException \InvalidArgumentException
151 */
152 public function setCompressionLevelThrowsExceptionIfCompressionLevelIsNotBetweenMinusOneAndNine() {
153 $this->setUpBackend(array('compressionLevel' => 11));
154 }
155
156 /**
157 * @test Functional
158 * @expectedException \InvalidArgumentException
159 */
160 public function setThrowsExceptionIfIdentifierIsNotAString() {
161 $this->setUpBackend();
162 $this->backend->set(array(), 'data');
163 }
164
165 /**
166 * @test Functional
167 * @expectedException \TYPO3\CMS\Core\Cache\Exception\InvalidDataException
168 */
169 public function setThrowsExceptionIfDataIsNotAString() {
170 $this->setUpBackend();
171 $this->backend->set('identifier' . uniqid(), array());
172 }
173
174 /**
175 * @test Functional
176 * @expectedException \InvalidArgumentException
177 */
178 public function setThrowsExceptionIfLifetimeIsNegative() {
179 $this->setUpBackend();
180 $this->backend->set('identifier' . uniqid(), 'data', array(), -42);
181 }
182
183 /**
184 * @test Functional
185 * @expectedException \InvalidArgumentException
186 */
187 public function setThrowsExceptionIfLifetimeIsNotNullOrAnInteger() {
188 $this->setUpBackend();
189 $this->backend->set('identifier' . uniqid(), 'data', array(), array());
190 }
191
192 /**
193 * @test Implementation
194 */
195 public function setStoresEntriesInSelectedDatabase() {
196 $this->setUpRedis();
197 $this->redis->select(1);
198 $this->setUpBackend(array('database' => 1));
199 $identifier = 'identifier' . uniqid();
200 $this->backend->set($identifier, 'data');
201 $this->assertTrue($this->redis->exists('identData:' . $identifier));
202 }
203
204 /**
205 * @test Implementation
206 */
207 public function setSavesStringDataTypeForIdentifierToDataEntry() {
208 $this->setUpBackend();
209 $this->setUpRedis();
210 $identifier = 'identifier' . uniqid();
211 $this->backend->set($identifier, 'data');
212 $this->assertSame(\Redis::REDIS_STRING, $this->redis->type('identData:' . $identifier));
213 }
214
215 /**
216 * @test Implementation
217 */
218 public function setSavesEntryWithDefaultLifeTime() {
219 $this->setUpBackend();
220 $this->setUpRedis();
221 $identifier = 'identifier' . uniqid();
222 $defaultLifetime = 42;
223 $this->backend->setDefaultLifetime($defaultLifetime);
224 $this->backend->set($identifier, 'data');
225 $lifetimeRegisteredInBackend = $this->redis->ttl('identData:' . $identifier);
226 $this->assertSame($defaultLifetime, $lifetimeRegisteredInBackend);
227 }
228
229 /**
230 * @test Implementation
231 */
232 public function setSavesEntryWithSpecifiedLifeTime() {
233 $this->setUpBackend();
234 $this->setUpRedis();
235 $identifier = 'identifier' . uniqid();
236 $lifetime = 43;
237 $this->backend->set($identifier, 'data', array(), $lifetime);
238 $lifetimeRegisteredInBackend = $this->redis->ttl('identData:' . $identifier);
239 $this->assertSame($lifetime, $lifetimeRegisteredInBackend);
240 }
241
242 /**
243 * @test Implementation
244 */
245 public function setSavesEntryWithUnlimitedLifeTime() {
246 $this->setUpBackend();
247 $this->setUpRedis();
248 $identifier = 'identifier' . uniqid();
249 $this->backend->set($identifier, 'data', array(), 0);
250 $lifetimeRegisteredInBackend = $this->redis->ttl('identData:' . $identifier);
251 $this->assertSame(31536000, $lifetimeRegisteredInBackend);
252 }
253
254 /**
255 * @test Functional
256 */
257 public function setOverwritesExistingEntryWithNewData() {
258 $this->setUpBackend();
259 $data = 'data 1';
260 $identifier = 'identifier' . uniqid();
261 $this->backend->set($identifier, $data);
262 $otherData = 'data 2';
263 $this->backend->set($identifier, $otherData);
264 $fetchedData = $this->backend->get($identifier);
265 $this->assertSame($otherData, $fetchedData);
266 }
267
268 /**
269 * @test Implementation
270 */
271 public function setOverwritesExistingEntryWithSpecifiedLifetime() {
272 $this->setUpBackend();
273 $this->setUpRedis();
274 $data = 'data';
275 $identifier = 'identifier' . uniqid();
276 $this->backend->set($identifier, $data);
277 $lifetime = 42;
278 $this->backend->set($identifier, $data, array(), $lifetime);
279 $lifetimeRegisteredInBackend = $this->redis->ttl('identData:' . $identifier);
280 $this->assertSame($lifetime, $lifetimeRegisteredInBackend);
281 }
282
283 /**
284 * @test Implementation
285 */
286 public function setOverwritesExistingEntryWithNewDefaultLifetime() {
287 $this->setUpBackend();
288 $this->setUpRedis();
289 $data = 'data';
290 $identifier = 'identifier' . uniqid();
291 $lifetime = 42;
292 $this->backend->set($identifier, $data, array(), $lifetime);
293 $newDefaultLifetime = 43;
294 $this->backend->setDefaultLifetime($newDefaultLifetime);
295 $this->backend->set($identifier, $data, array(), $newDefaultLifetime);
296 $lifetimeRegisteredInBackend = $this->redis->ttl('identData:' . $identifier);
297 $this->assertSame($newDefaultLifetime, $lifetimeRegisteredInBackend);
298 }
299
300 /**
301 * @test Implementation
302 */
303 public function setOverwritesExistingEntryWithNewUnlimitedLifetime() {
304 $this->setUpBackend();
305 $this->setUpRedis();
306 $data = 'data';
307 $identifier = 'identifier' . uniqid();
308 $lifetime = 42;
309 $this->backend->set($identifier, $data, array(), $lifetime);
310 $this->backend->set($identifier, $data, array(), 0);
311 $lifetimeRegisteredInBackend = $this->redis->ttl('identData:' . $identifier);
312 $this->assertSame(31536000, $lifetimeRegisteredInBackend);
313 }
314
315 /**
316 * @test Implementation
317 */
318 public function setSavesSetDataTypeForIdentifierToTagsSet() {
319 $this->setUpBackend();
320 $this->setUpRedis();
321 $identifier = 'identifier' . uniqid();
322 $this->backend->set($identifier, 'data', array('tag'));
323 $this->assertSame(\Redis::REDIS_SET, $this->redis->type('identTags:' . $identifier));
324 }
325
326 /**
327 * @test Implementation
328 */
329 public function setSavesSpecifiedTagsInIdentifierToTagsSet() {
330 $this->setUpBackend();
331 $this->setUpRedis();
332 $identifier = 'identifier' . uniqid();
333 $tags = array('thatTag', 'thisTag');
334 $this->backend->set($identifier, 'data', $tags);
335 $savedTags = $this->redis->sMembers('identTags:' . $identifier);
336 sort($savedTags);
337 $this->assertSame($tags, $savedTags);
338 }
339
340 /**
341 * @test Implementation
342 */
343 public function setRemovesAllPreviouslySetTagsFromIdentifierToTagsSet() {
344 $this->setUpBackend();
345 $this->setUpRedis();
346 $identifier = 'identifier' . uniqid();
347 $tags = array('fooTag', 'barTag');
348 $this->backend->set($identifier, 'data', $tags);
349 $this->backend->set($identifier, 'data', array());
350 $this->assertSame(array(), $this->redis->sMembers('identTags:' . $identifier));
351 }
352
353 /**
354 * @test Implementation
355 */
356 public function setRemovesMultiplePreviouslySetTagsFromIdentifierToTagsSet() {
357 $this->setUpBackend();
358 $this->setUpRedis();
359 $identifier = 'identifier' . uniqid();
360 $firstTagSet = array('tag1', 'tag2', 'tag3', 'tag4');
361 $this->backend->set($identifier, 'data', $firstTagSet);
362 $secondTagSet = array('tag1', 'tag3');
363 $this->backend->set($identifier, 'data', $secondTagSet);
364 $actualTagSet = $this->redis->sMembers('identTags:' . $identifier);
365 sort($actualTagSet);
366 $this->assertSame($secondTagSet, $actualTagSet);
367 }
368
369 /**
370 * @test Implementation
371 */
372 public function setSavesSetDataTypeForTagToIdentifiersSet() {
373 $this->setUpBackend();
374 $this->setUpRedis();
375 $identifier = 'identifier' . uniqid();
376 $tag = 'tag';
377 $this->backend->set($identifier, 'data', array($tag));
378 $this->assertSame(\Redis::REDIS_SET, $this->redis->type('tagIdents:' . $tag));
379 }
380
381 /**
382 * @test Implementation
383 */
384 public function setSavesIdentifierInTagToIdentifiersSetOfSpecifiedTag() {
385 $this->setUpBackend();
386 $this->setUpRedis();
387 $identifier = 'identifier' . uniqid();
388 $tag = 'thisTag';
389 $this->backend->set($identifier, 'data', array($tag));
390 $savedTagToIdentifiersMemberArray = $this->redis->sMembers('tagIdents:' . $tag);
391 $this->assertSame(array($identifier), $savedTagToIdentifiersMemberArray);
392 }
393
394 /**
395 * @test Implementation
396 */
397 public function setAppendsSecondIdentifierInTagToIdentifiersEntry() {
398 $this->setUpBackend();
399 $this->setUpRedis();
400 $firstIdentifier = 'identifier' . uniqid();
401 $tag = 'thisTag';
402 $this->backend->set($firstIdentifier, 'data', array($tag));
403 $secondIdentifier = 'identifier' . uniqid();
404 $this->backend->set($secondIdentifier, 'data', array($tag));
405 $savedTagToIdentifiersMemberArray = $this->redis->sMembers('tagIdents:' . $tag);
406 sort($savedTagToIdentifiersMemberArray);
407 $identifierArray = array($firstIdentifier, $secondIdentifier);
408 sort($identifierArray);
409 $this->assertSame(array($firstIdentifier, $secondIdentifier), $savedTagToIdentifiersMemberArray);
410 }
411
412 /**
413 * @test Implementation
414 */
415 public function setRemovesIdentifierFromTagToIdentifiersEntryIfTagIsOmittedOnConsecutiveSet() {
416 $this->setUpBackend();
417 $this->setUpRedis();
418 $identifier = 'identifier' . uniqid();
419 $tag = 'thisTag';
420 $this->backend->set($identifier, 'data', array($tag));
421 $this->backend->set($identifier, 'data', array());
422 $savedTagToIdentifiersMemberArray = $this->redis->sMembers('tagIdents:' . $tag);
423 $this->assertSame(array(), $savedTagToIdentifiersMemberArray);
424 }
425
426 /**
427 * @test Implementation
428 */
429 public function setAddsIdentifierInTagToIdentifiersEntryIfTagIsAddedOnConsecutiveSet() {
430 $this->setUpBackend();
431 $this->setUpRedis();
432 $identifier = 'identifier' . uniqid();
433 $this->backend->set($identifier, 'data');
434 $tag = 'thisTag';
435 $this->backend->set($identifier, 'data', array($tag));
436 $savedTagToIdentifiersMemberArray = $this->redis->sMembers('tagIdents:' . $tag);
437 $this->assertSame(array($identifier), $savedTagToIdentifiersMemberArray);
438 }
439
440 /**
441 * @test Implementation
442 */
443 public function setSavesCompressedDataWithEnabledCompression() {
444 $this->setUpBackend(array(
445 'compression' => TRUE
446 ));
447 $this->setUpRedis();
448 $identifier = 'identifier' . uniqid();
449 $data = 'some data ' . microtime();
450 $this->backend->set($identifier, $data);
451 $uncompresedStoredData = '';
452 try {
453 $uncompresedStoredData = @gzuncompress($this->redis->get(('identData:' . $identifier)));
454 } catch (\Exception $e) {
455
456 }
457 $this->assertEquals($data, $uncompresedStoredData, 'Original and compressed data don\'t match');
458 }
459
460 /**
461 * @test Implementation
462 */
463 public function setSavesPlaintextDataWithEnabledCompressionAndCompressionLevel0() {
464 $this->setUpBackend(array(
465 'compression' => TRUE,
466 'compressionLevel' => 0
467 ));
468 $this->setUpRedis();
469 $identifier = 'identifier' . uniqid();
470 $data = 'some data ' . microtime();
471 $this->backend->set($identifier, $data);
472 $this->assertGreaterThan(0, substr_count($this->redis->get('identData:' . $identifier), $data), 'Plaintext data not found');
473 }
474
475 /**
476 * @test Functional
477 * @expectedException \InvalidArgumentException
478 */
479 public function hasThrowsExceptionIfIdentifierIsNotAString() {
480 $this->setUpBackend();
481 $this->backend->has(array());
482 }
483
484 /**
485 * @test Functional
486 */
487 public function hasReturnsFalseForNotExistingEntry() {
488 $this->setUpBackend();
489 $identifier = 'identifier' . uniqid();
490 $this->assertFalse($this->backend->has($identifier));
491 }
492
493 /**
494 * @test Functional
495 */
496 public function hasReturnsTrueForPreviouslySetEntry() {
497 $this->setUpBackend();
498 $identifier = 'identifier' . uniqid();
499 $this->backend->set($identifier, 'data');
500 $this->assertTrue($this->backend->has($identifier));
501 }
502
503 /**
504 * @test Functional
505 * @expectedException \InvalidArgumentException
506 */
507 public function getThrowsExceptionIfIdentifierIsNotAString() {
508 $this->setUpBackend();
509 $this->backend->get(array());
510 }
511
512 /**
513 * @test Functional
514 */
515 public function getReturnsPreviouslyCompressedSetEntry() {
516 $this->setUpBackend(array(
517 'compression' => TRUE
518 ));
519 $data = 'data';
520 $identifier = 'identifier' . uniqid();
521 $this->backend->set($identifier, $data);
522 $fetchedData = $this->backend->get($identifier);
523 $this->assertSame($data, $fetchedData);
524 }
525
526 /**
527 * @test Functional
528 */
529 public function getReturnsPreviouslySetEntry() {
530 $this->setUpBackend();
531 $data = 'data';
532 $identifier = 'identifier' . uniqid();
533 $this->backend->set($identifier, $data);
534 $fetchedData = $this->backend->get($identifier);
535 $this->assertSame($data, $fetchedData);
536 }
537
538 /**
539 * @test Functional
540 * @expectedException \InvalidArgumentException
541 */
542 public function removeThrowsExceptionIfIdentifierIsNotAString() {
543 $this->setUpBackend();
544 $this->backend->remove(array());
545 }
546
547 /**
548 * @test Functional
549 */
550 public function removeReturnsFalseIfNoEntryWasDeleted() {
551 $this->setUpBackend();
552 $this->assertFalse($this->backend->remove('identifier' . uniqid()));
553 }
554
555 /**
556 * @test Functional
557 */
558 public function removeReturnsTrueIfAnEntryWasDeleted() {
559 $this->setUpBackend();
560 $identifier = 'identifier' . uniqid();
561 $this->backend->set($identifier, 'data');
562 $this->assertTrue($this->backend->remove($identifier));
563 }
564
565 /**
566 * @test Functional
567 */
568 public function removeDeletesEntryFromCache() {
569 $this->setUpBackend();
570 $identifier = 'identifier' . uniqid();
571 $this->backend->set($identifier, 'data');
572 $this->backend->remove($identifier);
573 $this->assertFalse($this->backend->has($identifier));
574 }
575
576 /**
577 * @test Implementation
578 */
579 public function removeDeletesIdentifierToTagEntry() {
580 $this->setUpBackend();
581 $this->setUpRedis();
582 $identifier = 'identifier' . uniqid();
583 $tag = 'thisTag';
584 $this->backend->set($identifier, 'data', array($tag));
585 $this->backend->remove($identifier);
586 $this->assertFalse($this->redis->exists('identTags:' . $identifier));
587 }
588
589 /**
590 * @test Implementation
591 */
592 public function removeDeletesIdentifierFromTagToIdentifiersSet() {
593 $this->setUpBackend();
594 $this->setUpRedis();
595 $identifier = 'identifier' . uniqid();
596 $tag = 'thisTag';
597 $this->backend->set($identifier, 'data', array($tag));
598 $this->backend->remove($identifier);
599 $tagToIdentifiersMemberArray = $this->redis->sMembers('tagIdents:' . $tag);
600 $this->assertSame(array(), $tagToIdentifiersMemberArray);
601 }
602
603 /**
604 * @test Implementation
605 */
606 public function removeDeletesIdentifierFromTagToIdentifiersSetWithMultipleEntries() {
607 $this->setUpBackend();
608 $this->setUpRedis();
609 $firstIdentifier = 'identifier' . uniqid();
610 $secondIdentifier = 'identifier' . uniqid();
611 $tag = 'thisTag';
612 $this->backend->set($firstIdentifier, 'data', array($tag));
613 $this->backend->set($secondIdentifier, 'data', array($tag));
614 $this->backend->remove($firstIdentifier);
615 $tagToIdentifiersMemberArray = $this->redis->sMembers('tagIdents:' . $tag);
616 $this->assertSame(array($secondIdentifier), $tagToIdentifiersMemberArray);
617 }
618
619 /**
620 * @test Functional
621 * @expectedException \InvalidArgumentException
622 */
623 public function findIdentifiersByTagThrowsExceptionIfTagIsNotAString() {
624 $this->setUpBackend();
625 $this->backend->findIdentifiersByTag(array());
626 }
627
628 /**
629 * @test Functional
630 */
631 public function findIdentifiersByTagReturnsEmptyArrayForNotExistingTag() {
632 $this->setUpBackend();
633 $this->assertSame(array(), $this->backend->findIdentifiersByTag('thisTag'));
634 }
635
636 /**
637 * @test Functional
638 */
639 public function findIdentifiersByTagReturnsAllIdentifiersTagedWithSpecifiedTag() {
640 $this->setUpBackend();
641 $firstIdentifier = 'identifier' . uniqid();
642 $secondIdentifier = 'identifier' . uniqid();
643 $thirdIdentifier = 'identifier' . uniqid();
644 $tagsForFirstIdentifier = array('thisTag');
645 $tagsForSecondIdentifier = array('thatTag');
646 $tagsForThirdIdentifier = array('thisTag', 'thatTag');
647 $this->backend->set($firstIdentifier, 'data', $tagsForFirstIdentifier);
648 $this->backend->set($secondIdentifier, 'data', $tagsForSecondIdentifier);
649 $this->backend->set($thirdIdentifier, 'data', $tagsForThirdIdentifier);
650 $expectedResult = array($firstIdentifier, $thirdIdentifier);
651 $actualResult = $this->backend->findIdentifiersByTag('thisTag');
652 sort($actualResult);
653 $this->assertSame($expectedResult, $actualResult);
654 }
655
656 /**
657 * @test Implementation
658 */
659 public function flushRemovesAllEntriesFromCache() {
660 $this->setUpBackend();
661 $this->setUpRedis();
662 $identifier = 'identifier' . uniqid();
663 $this->backend->set($identifier, 'data');
664 $this->backend->flush();
665 $this->assertSame(array(), $this->redis->getKeys('*'));
666 }
667
668 /**
669 * @test Functional
670 * @expectedException \InvalidArgumentException
671 */
672 public function flushByTagThrowsExceptionIfTagIsNotAString() {
673 $this->setUpBackend();
674 $this->backend->flushByTag(array());
675 }
676
677 /**
678 * @test Functional
679 */
680 public function flushByTagRemovesEntriesTaggedWithSpecifiedTag() {
681 $this->setUpBackend();
682 $identifier = 'identifier' . uniqid();
683 $this->backend->set($identifier . 'A', 'data', array('tag1'));
684 $this->backend->set($identifier . 'B', 'data', array('tag2'));
685 $this->backend->set($identifier . 'C', 'data', array('tag1', 'tag2'));
686 $this->backend->flushByTag('tag1');
687 $expectedResult = array(FALSE, TRUE, FALSE);
688 $actualResult = array(
689 $this->backend->has($identifier . 'A'),
690 $this->backend->has($identifier . 'B'),
691 $this->backend->has($identifier . 'C')
692 );
693 $this->assertSame($expectedResult, $actualResult);
694 }
695
696 /**
697 * @test Implementation
698 */
699 public function flushByTagRemovesTemporarySet() {
700 $this->setUpBackend();
701 $this->setUpRedis();
702 $identifier = 'identifier' . uniqid();
703 $this->backend->set($identifier . 'A', 'data', array('tag1'));
704 $this->backend->set($identifier . 'C', 'data', array('tag1', 'tag2'));
705 $this->backend->flushByTag('tag1');
706 $this->assertSame(array(), $this->redis->getKeys('temp*'));
707 }
708
709 /**
710 * @test Implementation
711 */
712 public function flushByTagRemovesIdentifierToTagsSetOfEntryTaggedWithGivenTag() {
713 $this->setUpBackend();
714 $this->setUpRedis();
715 $identifier = 'identifier' . uniqid();
716 $tag = 'tag1';
717 $this->backend->set($identifier, 'data', array($tag));
718 $this->backend->flushByTag($tag);
719 $this->assertFalse($this->redis->exists('identTags:' . $identifier));
720 }
721
722 /**
723 * @test Implementation
724 */
725 public function flushByTagDoesNotRemoveIdentifierToTagsSetOfUnrelatedEntry() {
726 $this->setUpBackend();
727 $this->setUpRedis();
728 $identifierToBeRemoved = 'identifier' . uniqid();
729 $tagToRemove = 'tag1';
730 $this->backend->set($identifierToBeRemoved, 'data', array($tagToRemove));
731 $identifierNotToBeRemoved = 'identifier' . uniqid();
732 $tagNotToRemove = 'tag2';
733 $this->backend->set($identifierNotToBeRemoved, 'data', array($tagNotToRemove));
734 $this->backend->flushByTag($tagToRemove);
735 $this->assertSame(array($tagNotToRemove), $this->redis->sMembers('identTags:' . $identifierNotToBeRemoved));
736 }
737
738 /**
739 * @test Implementation
740 */
741 public function flushByTagRemovesTagToIdentifiersSetOfGivenTag() {
742 $this->setUpBackend();
743 $this->setUpRedis();
744 $identifier = 'identifier' . uniqid();
745 $tag = 'tag1';
746 $this->backend->set($identifier, 'data', array($tag));
747 $this->backend->flushByTag($tag);
748 $this->assertFalse($this->redis->exists('tagIdents:' . $tag));
749 }
750
751 /**
752 * @test Implementation
753 */
754 public function flushByTagRemovesIdentifiersTaggedWithGivenTagFromTagToIdentifiersSets() {
755 $this->setUpBackend();
756 $this->setUpRedis();
757 $identifier = 'identifier' . uniqid();
758 $this->backend->set($identifier . 'A', 'data', array('tag1', 'tag2'));
759 $this->backend->set($identifier . 'B', 'data', array('tag1', 'tag2'));
760 $this->backend->set($identifier . 'C', 'data', array('tag2'));
761 $this->backend->flushByTag('tag1');
762 $this->assertSame(array($identifier . 'C'), $this->redis->sMembers('tagIdents:tag2'));
763 }
764
765 /**
766 * @test Implementation
767 */
768 public function collectGarbageDoesNotRemoveNotExpiredIdentifierToDataEntry() {
769 $this->setUpBackend();
770 $this->setUpRedis();
771 $identifier = 'identifier' . uniqid();
772 $this->backend->set($identifier . 'A', 'data', array('tag'));
773 $this->backend->set($identifier . 'B', 'data', array('tag'));
774 $this->redis->delete('identData:' . $identifier . 'A');
775 $this->backend->collectGarbage();
776 $this->assertTrue($this->redis->exists('identData:' . $identifier . 'B'));
777 }
778
779 /**
780 * @test Implementation
781 */
782 public function collectGarbageRemovesLeftOverIdentifierToTagsSet() {
783 $this->setUpBackend();
784 $this->setUpRedis();
785 $identifier = 'identifier' . uniqid();
786 $this->backend->set($identifier . 'A', 'data', array('tag'));
787 $this->backend->set($identifier . 'B', 'data', array('tag'));
788 $this->redis->delete('identData:' . $identifier . 'A');
789 $this->backend->collectGarbage();
790 $expectedResult = array(FALSE, TRUE);
791 $actualResult = array(
792 $this->redis->exists('identTags:' . $identifier . 'A'),
793 $this->redis->exists('identTags:' . $identifier . 'B')
794 );
795 $this->assertSame($expectedResult, $actualResult);
796 }
797
798 /**
799 * @test Implementation
800 */
801 public function collectGarbageRemovesExpiredIdentifierFromTagsToIdentifierSet() {
802 $this->setUpBackend();
803 $this->setUpRedis();
804 $identifier = 'identifier' . uniqid();
805 $this->backend->set($identifier . 'A', 'data', array('tag1', 'tag2'));
806 $this->backend->set($identifier . 'B', 'data', array('tag2'));
807 $this->redis->delete('identData:' . $identifier . 'A');
808 $this->backend->collectGarbage();
809 $expectedResult = array(
810 array(),
811 array($identifier . 'B')
812 );
813 $actualResult = array(
814 $this->redis->sMembers('tagIdents:tag1'),
815 $this->redis->sMembers('tagIdents:tag2')
816 );
817 $this->assertSame($expectedResult, $actualResult);
818 }
819
820 }