2 /***************************************************************
5 * (c) 2009-2011 Ingo Renner <ingo@typo3.org>
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
17 * This script is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * This copyright notice MUST APPEAR in all copies of the script!
23 ***************************************************************/
26 * Testcase for the DB cache backend
28 * @author Ingo Renner <ingo@typo3.org>
32 class t3lib_cache_backend_DbBackendTest
extends tx_phpunit_testcase
{
35 * Enable backup of global and system variables
39 protected $backupGlobals = TRUE;
42 * Exclude TYPO3_DB from backup/ restore of $GLOBALS
43 * because resource types cannot be handled during serializing
47 protected $backupGlobalsBlacklist = array('TYPO3_DB');
50 * @var t3lib_DB Backup of original TYPO3_DB instance
52 protected $typo3DbBackup;
55 * @var string Name of the testing data table
57 protected $testingCacheTable = 'cachingframework_Testing';
60 * @var string Name of the testing tags table
62 protected $testingTagsTable = 'cachingframework_Testing_tags';
67 public function setUp() {
68 $this->typo3DbBackup
= $GLOBALS['TYPO3_DB'];
72 * Sets up the backend used for testing
75 * @author Ingo Renner <ingo@typo3.org>
76 * @author Christian Kuhn <lolli@schwarzbu.ch>
78 protected function setUpBackend(array $backendOptions = array()) {
79 $GLOBALS['TYPO3_DB']->sql_query('CREATE TABLE ' . $this->testingCacheTable
. ' (
80 id int(11) unsigned NOT NULL auto_increment,
81 identifier varchar(250) DEFAULT \'\' NOT NULL,
82 expires int(11) unsigned DEFAULT \'0\' NOT NULL,
85 KEY cache_id (identifier, expires)
89 $GLOBALS['TYPO3_DB']->sql_query('CREATE TABLE ' . $this->testingTagsTable
. ' (
90 id int(11) unsigned NOT NULL auto_increment,
91 identifier varchar(250) DEFAULT \'\' NOT NULL,
92 tag varchar(250) DEFAULT \'\' NOT NULL,
94 KEY cache_id (identifier),
99 $backend = t3lib_div
::makeInstance(
100 't3lib_cache_backend_DbBackend',
109 * Helper method to inject a mock frontend to backend instance
111 * @param t3lib_cache_backend_DbBackend $backend Current backend instance
112 * @return t3lib_cache_frontend_Frontend Mock frontend
114 protected function setUpMockFrontendOfBackend(t3lib_cache_backend_DbBackend
$backend) {
115 $mockCache = $this->getMock('t3lib_cache_frontend_AbstractFrontend', array(), array(), '', FALSE);
116 $mockCache->expects($this->any())->method('getIdentifier')->will($this->returnValue('Testing'));
117 $backend->setCache($mockCache);
123 * @author Ingo Renner <ingo@typo3.org>
125 public function tearDown() {
126 $GLOBALS['TYPO3_DB']->sql_query(
127 'DROP TABLE IF EXISTS ' . $this->testingCacheTable
. ';'
130 $GLOBALS['TYPO3_DB']->sql_query(
131 'DROP TABLE IF EXISTS ' . $this->testingTagsTable
. ';'
134 $GLOBALS['TYPO3_DB'] = $this->typo3DbBackup
;
139 * @author Ingo Renner <ingo@typo3.org>
141 public function setCacheCalculatesCacheTableName() {
142 $backend = $this->setUpBackend();
143 $this->setUpMockFrontendOfBackend($backend);
144 $this->assertEquals($this->testingCacheTable
, $backend->getCacheTable());
150 public function setCacheCalculatesTagsTableName() {
151 $backend = $this->setUpBackend();
152 $this->setUpMockFrontendOfBackend($backend);
154 $this->assertEquals($this->testingTagsTable
, $backend->getTagsTable());
159 * @expectedException t3lib_cache_Exception
161 public function setThrowsExceptionIfFrontendWasNotSet() {
162 $backend = $this->setUpBackend();
163 $backend->set('identifier', 'data');
168 * @expectedException t3lib_cache_exception_InvalidData
169 * @author Ingo Renner <ingo@typo3.org>
171 public function setThrowsExceptionIfDataIsNotAString() {
172 $backend = $this->setUpBackend();
173 $this->setUpMockFrontendOfBackend($backend);
175 $data = array('Some data');
176 $entryIdentifier = 'BackendDbTest';
178 $backend->set($entryIdentifier, $data);
183 * @author Ingo Renner <ingo@typo3.org>
185 public function setInsertsEntryInTable() {
186 $backend = $this->setUpBackend();
187 $this->setUpMockFrontendOfBackend($backend);
189 $data = 'some data' . microtime();
190 $entryIdentifier = 'BackendDbTest';
192 $backend->set($entryIdentifier, $data);
194 $entryFound = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow(
196 $this->testingCacheTable
,
197 'identifier = \'' . $entryIdentifier . '\''
200 $this->assertEquals($data, $entryFound['content']);
205 * @author Ingo Renner <ingo@typo3.org>
207 public function setRemovesAnAlreadyExistingCacheEntryForTheSameIdentifier() {
208 $backend = $this->setUpBackend();
209 $this->setUpMockFrontendOfBackend($backend);
211 $data1 = 'some data' . microtime();
212 $data2 = $data1 . '_different';
213 $entryIdentifier = 'BackendDbRemoveBeforeSetTest';
215 $backend->set($entryIdentifier, $data1, array(), 500);
216 $backend->set($entryIdentifier, $data2, array(), 200);
218 $entriesFound = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
220 $this->testingCacheTable
,
221 'identifier = \'' . $entryIdentifier . '\''
224 $this->assertEquals(1, count($entriesFound));
229 * @author Ingo Renner <ingo@typo3.org>
231 public function setReallySavesSpecifiedTags() {
232 $backend = $this->setUpBackend();
233 $this->setUpMockFrontendOfBackend($backend);
235 $data = 'some data' . microtime();
236 $entryIdentifier = 'BackendDbTest';
238 $backend->set($entryIdentifier, $data, array('UnitTestTag%tag1', 'UnitTestTag%tag2'));
240 $entriesFound = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
242 $this->testingTagsTable
,
243 'identifier = \'' . $entryIdentifier . '\''
248 foreach ($entriesFound as $entry) {
249 $tags[] = $entry['tag'];
252 $this->assertTrue(count($tags) > 0);
253 $this->assertTrue(in_array('UnitTestTag%tag1', $tags));
254 $this->assertTrue(in_array('UnitTestTag%tag2', $tags));
259 * @author Christian Kuhn <lolli@schwarzbu.ch>
261 public function setSavesCompressedDataWithEnabledCompression() {
262 $backend = $this->setUpBackend(
264 'compression' => TRUE,
267 $this->setUpMockFrontendOfBackend($backend);
269 $data = 'some data ' . microtime();
270 $entryIdentifier = 'BackendDbTest';
272 $backend->set($entryIdentifier, $data);
274 $entry = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow(
276 $this->testingCacheTable
,
277 'identifier = \'' . $entryIdentifier . '\''
280 $this->assertEquals($data, @gzuncompress
($entry['content']));
285 * @author Christian Kuhn <lolli@schwarzbu.ch>
287 public function setSavesPlaintextDataWithEnabledCompressionAndCompressionLevel0() {
288 $backend = $this->setUpBackend(
290 'compression' => TRUE,
291 'compressionLevel' => 0,
294 $this->setUpMockFrontendOfBackend($backend);
296 $data = 'some data ' . microtime();
297 $entryIdentifier = 'BackendDbTest';
299 $backend->set($entryIdentifier, $data);
301 $entry = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow(
303 $this->testingCacheTable
,
304 'identifier = \'' . $entryIdentifier . '\''
307 $this->assertGreaterThan(0, substr_count($entry['content'], $data));
312 * @expectedException t3lib_cache_Exception
314 public function getThrowsExceptionIfFrontendWasNotSet() {
315 $backend = $this->setUpBackend();
316 $backend->get('identifier');
321 * @author Ingo Renner <ingo@typo3.org>
323 public function getReturnsContentOfTheCorrectCacheEntry() {
324 $backend = $this->setUpBackend();
325 $this->setUpMockFrontendOfBackend($backend);
327 $data = 'some data' . microtime();
328 $entryIdentifier = 'BackendDbTest';
330 $backend->set($entryIdentifier, $data, array(), 500);
332 $data = 'some other data' . microtime();
333 $backend->set($entryIdentifier, $data, array(), 100);
335 $loadedData = $backend->get($entryIdentifier);
337 $this->assertEquals($data, $loadedData);
342 * @expectedException t3lib_cache_Exception
344 public function hasThrowsExceptionIfFrontendWasNotSet() {
345 $backend = $this->setUpBackend();
346 $backend->has('identifier');
351 * @author Ingo Renner <ingo@typo3.org>
353 public function hasReturnsTheCorrectResult() {
354 $backend = $this->setUpBackend();
355 $this->setUpMockFrontendOfBackend($backend);
357 $data = 'some data' . microtime();
358 $entryIdentifier = 'BackendDbTest';
360 $backend->set($entryIdentifier, $data);
362 $this->assertTrue($backend->has($entryIdentifier));
363 $this->assertFalse($backend->has($entryIdentifier . 'Not'));
368 * @expectedException t3lib_cache_Exception
370 public function removeThrowsExceptionIfFrontendWasNotSet() {
371 $backend = $this->setUpBackend();
372 $backend->remove('identifier');
377 * @author Ingo Renner <ingo@typo3.org>
379 public function removeReallyRemovesACacheEntry() {
380 $backend = $this->setUpBackend();
381 $this->setUpMockFrontendOfBackend($backend);
383 $data = 'some data' . microtime();
384 $entryIdentifier = 'BackendDbRemovalTest';
386 $backend->set($entryIdentifier, $data);
388 $backend->remove($entryIdentifier);
390 $entriesFound = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
392 $this->testingCacheTable
,
393 'identifier = \'' . $entryIdentifier . '\''
396 $this->assertTrue(count($entriesFound) == 0);
401 * @expectedException t3lib_cache_Exception
403 public function collectGarbageThrowsExceptionIfFrontendWasNotSet() {
404 $backend = $this->setUpBackend();
405 $backend->collectGarbage();
410 * @author Ingo Renner <ingo@typo3.org>
412 public function collectGarbageReallyRemovesAnExpiredCacheEntry() {
413 $backend = $this->setUpBackend();
414 $mockCache = $this->setUpMockFrontendOfBackend($backend);
416 $data = 'some data' . microtime();
417 $entryIdentifier = 'BackendDbRemovalTest';
419 $backend->set($entryIdentifier, $data, array(), 1);
421 $GLOBALS['EXEC_TIME'] +
= 2;
422 // setCache calls initializeCommonReferences which recalculate expire statement
423 // needed after manual $GLOBALS['EXEC_TIME'] manipulation
424 $backend->setCache($mockCache);
426 $backend->collectGarbage();
428 $entriesFound = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
430 $this->testingCacheTable
,
431 'identifier = \'' . $entryIdentifier . '\''
434 $this->assertTrue(count($entriesFound) == 0);
439 * @author Ingo Renner <ingo@typo3.org>
441 public function collectGarbageReallyRemovesAllExpiredCacheEntries() {
442 $backend = $this->setUpBackend();
443 $mockCache = $this->setUpMockFrontendOfBackend($backend);
445 $data = 'some data' . microtime();
446 $entryIdentifier = 'BackendDbRemovalTest';
448 $backend->set($entryIdentifier . 'A', $data, array(), 1);
449 $backend->set($entryIdentifier . 'B', $data, array(), 1);
450 $backend->set($entryIdentifier . 'C', $data, array(), 1);
452 $GLOBALS['EXEC_TIME'] +
= 2;
453 // setCache calls initializeCommonReferences which recalculate expire statement
454 // needed after manual $GLOBALS['EXEC_TIME'] manipulation
455 $backend->setCache($mockCache);
457 $backend->collectGarbage();
459 $entriesFound = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
461 $this->testingCacheTable
,
465 $this->assertTrue(count($entriesFound) == 0);
470 * @author Ingo Renner <ingo@typo3.org>
472 public function findIdentifiersByTagFindsCacheEntriesWithSpecifiedTag() {
473 $backend = $this->setUpBackend();
474 $this->setUpMockFrontendOfBackend($backend);
476 $data = 'some data' . microtime();
477 $backend->set('BackendDbTest1', $data, array('UnitTestTag%test', 'UnitTestTag%boring'));
478 $backend->set('BackendDbTest2', $data, array('UnitTestTag%test', 'UnitTestTag%special'));
479 $backend->set('BackendDbTest3', $data, array('UnitTestTag%test'));
481 $expectedEntry = 'BackendDbTest2';
483 $actualEntries = $backend->findIdentifiersByTag('UnitTestTag%special');
484 $this->assertTrue(is_array($actualEntries));
485 $this->assertEquals($expectedEntry, array_pop($actualEntries));
490 * @expectedException t3lib_cache_Exception
492 public function flushThrowsExceptionIfFrontendWasNotSet() {
493 $backend = $this->setUpBackend();
499 * @author Ingo Renner <ingo@typo3.org>
501 public function flushRemovesAllCacheEntries() {
502 $backend = $this->setUpBackend();
503 $this->setUpMockFrontendOfBackend($backend);
505 $data = 'some data' . microtime();
506 $backend->set('BackendDbTest1', $data, array('UnitTestTag%test'));
507 $backend->set('BackendDbTest2', $data, array('UnitTestTag%test', 'UnitTestTag%special'));
508 $backend->set('BackendDbTest3', $data, array('UnitTestTag%test'));
512 $entriesFound = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
514 $this->testingCacheTable
,
518 $this->assertTrue(count($entriesFound) == 0);
524 public function flushDropsDataTable() {
525 $backend = $this->setUpBackend();
526 $this->setUpMockFrontendOfBackend($backend);
528 $GLOBALS['TYPO3_DB'] = $this->getMock('t3lib_DB', array('admin_query'));
529 $GLOBALS['TYPO3_DB']->expects($this->at(0))
530 ->method('admin_query')
531 ->with('DROP TABLE IF EXISTS cachingframework_Testing');
539 public function flushDropsTagsTable() {
540 $backend = $this->setUpBackend();
541 $this->setUpMockFrontendOfBackend($backend);
543 $GLOBALS['TYPO3_DB'] = $this->getMock('t3lib_DB', array('admin_query'));
544 $GLOBALS['TYPO3_DB']->expects($this->at(1))
545 ->method('admin_query')
546 ->with('DROP TABLE IF EXISTS cachingframework_Testing_tags');
554 public function flushCreatesDataTable() {
555 $backend = $this->setUpBackend();
556 $this->setUpMockFrontendOfBackend($backend);
558 $GLOBALS['TYPO3_DB'] = $this->getMock('t3lib_DB', array('admin_query'));
559 $GLOBALS['TYPO3_DB']->expects($this->at(2))
560 ->method('admin_query')
561 ->will($this->returnCallback(array($this, flushCreatesDataTableCallback
)));
567 * Callback of flushCreatesDataTable to check if data table is created
569 * @param string $sql SQL of admin_query
572 public function flushCreatesDataTableCallback($sql) {
573 $startOfStatement = 'CREATE TABLE cachingframework_Testing (';
574 $this->assertEquals($startOfStatement, substr($sql, 0, strlen($startOfStatement)));
580 public function flushCreatesTagsTable() {
581 $backend = $this->setUpBackend();
582 $this->setUpMockFrontendOfBackend($backend);
584 $GLOBALS['TYPO3_DB'] = $this->getMock('t3lib_DB', array('admin_query'));
585 $GLOBALS['TYPO3_DB']->expects($this->at(3))
586 ->method('admin_query')
587 ->will($this->returnCallback(array($this, flushCreatesTagsTableCallback
)));
593 * Callback of flushCreatesTagsTable to check if tags table is created
595 * @param string $sql SQL of admin_query
598 public function flushCreatesTagsTableCallback($sql) {
599 $startOfStatement = 'CREATE TABLE cachingframework_Testing_tags (';
600 $this->assertEquals($startOfStatement, substr($sql, 0, strlen($startOfStatement)));
605 * @expectedException t3lib_cache_Exception
607 public function flushByTagThrowsExceptionIfFrontendWasNotSet() {
608 $backend = $this->setUpBackend();
609 $backend->flushByTag(array());
614 * @author Ingo Renner <ingo@typo3.org>
616 public function flushByTagRemovesCacheEntriesWithSpecifiedTag() {
617 $backend = $this->setUpBackend();
618 $this->setUpMockFrontendOfBackend($backend);
620 $data = 'some data' . microtime();
621 $backend->set('BackendDbTest1', $data, array('UnitTestTag%test', 'UnitTestTag%boring'));
622 $backend->set('BackendDbTest2', $data, array('UnitTestTag%test', 'UnitTestTag%special'));
623 $backend->set('BackendDbTest3', $data, array('UnitTestTag%test'));
625 $backend->flushByTag('UnitTestTag%special');
627 $this->assertTrue($backend->has('BackendDbTest1'), 'BackendDbTest1 does not exist anymore.');
628 $this->assertFalse($backend->has('BackendDbTest2'), 'BackendDbTest2 still exists.');
629 $this->assertTrue($backend->has('BackendDbTest3'), 'BackendDbTest3 does not exist anymore.');
631 $tagEntriesFound = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
633 $this->testingTagsTable
,
634 'tag = ' . $GLOBALS['TYPO3_DB']->fullQuoteStr('UnitTestTag%special', $this->testingTagsTable
)
636 $this->assertEquals(0, count($tagEntriesFound));
642 * @author Ingo Renner <ingo@typo3.org>
644 public function hasReturnsTheCorrectResultForEntryWithExceededLifetime() {
645 $backend = $this->setUpBackend();
646 $mockCache = $this->setUpMockFrontendOfBackend($backend);
648 $data = 'some data' . microtime();
649 $entryIdentifier = 'BackendDbTest';
651 $backend->set($entryIdentifier, $data);
653 $expiredEntryIdentifier = 'ExpiredBackendDbTest';
654 $expiredData = 'some old data' . microtime();
655 $backend->set($expiredEntryIdentifier, $expiredData, array(), 1);
657 $GLOBALS['EXEC_TIME'] +
= 2;
658 // setCache calls initializeCommonReferences which recalculate expire statement
659 // needed after manual $GLOBALS['EXEC_TIME'] manipulation
660 $backend->setCache($mockCache);
662 $this->assertFalse($backend->has($expiredEntryIdentifier));
667 * @author Christian Kuhn <lolli@schwarzbu.ch>
669 public function hasReturnsTrueForEntryWithUnlimitedLifetime() {
670 $backend = $this->setUpBackend();
671 $mockCache = $this->setUpMockFrontendOfBackend($backend);
673 $entryIdentifier = 'BackendDbTest';
675 $backend->set($entryIdentifier, 'data', array(), 0);
677 $GLOBALS['EXEC_TIME'] +
= 1;
678 // setCache calls initializeCommonReferences which recalculate expire statement
679 // needed after manual $GLOBALS['EXEC_TIME'] manipulation
680 $backend->setCache($mockCache);
682 $this->assertTrue($backend->has($entryIdentifier));
687 * @author Ingo Renner <ingo@typo3.org>
689 public function getReturnsFalseForEntryWithExceededLifetime() {
690 $backend = $this->setUpBackend();
691 $mockCache = $this->setUpMockFrontendOfBackend($backend);
693 $data = 'some data' . microtime();
694 $entryIdentifier = 'BackendDbTest';
696 $backend->set($entryIdentifier, $data);
698 $expiredEntryIdentifier = 'ExpiredBackendDbTest';
699 $expiredData = 'some old data' . microtime();
700 $backend->set($expiredEntryIdentifier, $expiredData, array(), 1);
702 $GLOBALS['EXEC_TIME'] +
= 2;
703 // setCache calls initializeCommonReferences which recalculate expire statement
704 // needed after manual $GLOBALS['EXEC_TIME'] manipulation
705 $backend->setCache($mockCache);
707 $this->assertEquals($data, $backend->get($entryIdentifier));
708 $this->assertFalse($backend->get($expiredEntryIdentifier));
713 * @expectedException t3lib_cache_Exception
715 public function findIdentifiersByTagThrowsExceptionIfFrontendWasNotSet() {
716 $backend = $this->setUpBackend();
717 $backend->findIdentifiersByTag('identifier');
722 * @author Ingo Renner <ingo@typo3.org>
724 public function findIdentifiersByTagReturnsEmptyArrayForEntryWithExceededLifetime() {
725 $backend = $this->setUpBackend();
726 $mockCache = $this->setUpMockFrontendOfBackend($backend);
728 $backend->set('BackendDbTest', 'some data', array('UnitTestTag%special'), 1);
730 $GLOBALS['EXEC_TIME'] +
= 2;
731 // setCache calls initializeCommonReferences which recalculate expire statement
732 // needed after manual $GLOBALS['EXEC_TIME'] manipulation
733 $backend->setCache($mockCache);
735 $this->assertEquals(array(), $backend->findIdentifiersByTag('UnitTestTag%special'));
740 * @author Ingo Renner <ingo@typo3.org>
742 public function setWithUnlimitedLifetimeWritesCorrectEntry() {
743 $backend = $this->setUpBackend();
744 $this->setUpMockFrontendOfBackend($backend);
746 $data = 'some data' . microtime();
747 $entryIdentifier = 'BackendFileTest';
749 $backend->set($entryIdentifier, $data, array(), 0);
751 $entryFound = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow(
753 $this->testingCacheTable
,
757 $this->assertTrue(is_array($entryFound));
759 $retrievedData = $entryFound['content'];
760 $this->assertEquals($data, $retrievedData);