diff --git a/.travis.yml b/.travis.yml index 78c29817db..6e7ac1c2e0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,8 @@ php: services: - mysql + - redis-server + - memcached env: - MYSQL_HOST=localhost MYSQL_PORT=3306 MYSQL_USERNAME=travis MYSQL_PASSWORD= MYSQL_DATABASE=test @@ -17,3 +19,5 @@ install: before_script: - mysql -e 'CREATE DATABASE IF NOT EXISTS test;' - mysql -utravis test < database.sql + - echo "extension=redis.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini + - echo "extension=memcached.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini diff --git a/database.sql b/database.sql index b3d8f5dd05..e51f2a1e9b 100644 --- a/database.sql +++ b/database.sql @@ -576,8 +576,10 @@ CREATE TABLE IF NOT EXISTS `locks` ( `id` int unsigned NOT NULL auto_increment COMMENT 'sequential ID', `name` varchar(128) NOT NULL DEFAULT '' COMMENT '', `locked` boolean NOT NULL DEFAULT '0' COMMENT '', + `expires` datetime NOT NULL DEFAULT '0001-01-01 00:00:00' COMMENT 'datetime of lock expiration', `pid` int unsigned NOT NULL DEFAULT 0 COMMENT 'Process ID', - PRIMARY KEY(`id`) + PRIMARY KEY(`id`), + INDEX `name_expires` (`name`,`expires`) ) DEFAULT COLLATE utf8mb4_general_ci COMMENT=''; -- diff --git a/include/dba.php b/include/dba.php index 9d828f8b44..061f5399c7 100644 --- a/include/dba.php +++ b/include/dba.php @@ -1109,7 +1109,7 @@ class dba { // Split the SQL queries in chunks of 100 values // We do the $i stuff here to make the code better readable - $i = $counter[$key_table][$key_condition]; + $i = isset($counter[$key_table][$key_condition]) ? $counter[$key_table][$key_condition] : 0; if (isset($compacted[$key_table][$key_condition][$i]) && count($compacted[$key_table][$key_condition][$i]) > 100) { ++$i; } diff --git a/src/Core/Cache/AbstractCacheDriver.php b/src/Core/Cache/AbstractCacheDriver.php index 417accdce7..15b822dc3b 100644 --- a/src/Core/Cache/AbstractCacheDriver.php +++ b/src/Core/Cache/AbstractCacheDriver.php @@ -18,6 +18,7 @@ abstract class AbstractCacheDriver extends BaseObject * @return string The cache key used for the cache */ protected function getCacheKey($key) { + // We fetch with the hostname as key to avoid problems with other applications return self::getApp()->get_hostname() . ":" . $key; } } diff --git a/src/Core/Cache/ArrayCache.php b/src/Core/Cache/ArrayCache.php index ec9f3a2577..b198287148 100644 --- a/src/Core/Cache/ArrayCache.php +++ b/src/Core/Cache/ArrayCache.php @@ -51,7 +51,7 @@ class ArrayCache extends AbstractCacheDriver implements IMemoryCacheDriver /** * (@inheritdoc) */ - public function clear() + public function clear($outdated = true) { $this->cachedData = []; return true; diff --git a/src/Core/Cache/DatabaseCacheDriver.php b/src/Core/Cache/DatabaseCacheDriver.php index 5c71fb1962..059fef3290 100644 --- a/src/Core/Cache/DatabaseCacheDriver.php +++ b/src/Core/Cache/DatabaseCacheDriver.php @@ -37,7 +37,7 @@ class DatabaseCacheDriver extends AbstractCacheDriver implements ICacheDriver { $fields = [ 'v' => serialize($value), - 'expires' => DateTimeFormat::utc('now + ' . $ttl . ' seconds'), + 'expires' => DateTimeFormat::utc('now + ' . $ttl . 'seconds'), 'updated' => DateTimeFormat::utcNow() ]; @@ -49,8 +49,12 @@ class DatabaseCacheDriver extends AbstractCacheDriver implements ICacheDriver return dba::delete('cache', ['k' => $key]); } - public function clear() + public function clear($outdated = true) { - return dba::delete('cache', ['`expires` < NOW()']); + if ($outdated) { + return dba::delete('cache', ['`expires` < NOW()']); + } else { + return dba::delete('cache', ['`k` IS NOT NULL ']); + } } } diff --git a/src/Core/Cache/ICacheDriver.php b/src/Core/Cache/ICacheDriver.php index ced7b4e216..e83b921c6a 100644 --- a/src/Core/Cache/ICacheDriver.php +++ b/src/Core/Cache/ICacheDriver.php @@ -42,8 +42,9 @@ interface ICacheDriver /** * Remove outdated data from the cache + * @param boolean $outdated just remove outdated values * * @return bool */ - public function clear(); + public function clear($outdated = true); } diff --git a/src/Core/Cache/MemcacheCacheDriver.php b/src/Core/Cache/MemcacheCacheDriver.php index af7e5ab0e2..bff543b542 100644 --- a/src/Core/Cache/MemcacheCacheDriver.php +++ b/src/Core/Cache/MemcacheCacheDriver.php @@ -96,9 +96,13 @@ class MemcacheCacheDriver extends AbstractCacheDriver implements IMemoryCacheDri /** * (@inheritdoc) */ - public function clear() + public function clear($outdated = true) { - return $this->memcache->flush(); + if ($outdated) { + return true; + } else { + return $this->memcache->flush(); + } } /** @@ -107,6 +111,6 @@ class MemcacheCacheDriver extends AbstractCacheDriver implements IMemoryCacheDri public function add($key, $value, $ttl = Cache::FIVE_MINUTES) { $cachekey = $this->getCacheKey($key); - return $this->memcache->add($cachekey, $value, $ttl); + return $this->memcache->add($cachekey, serialize($value), MEMCACHE_COMPRESSED, $ttl); } } diff --git a/src/Core/Cache/MemcachedCacheDriver.php b/src/Core/Cache/MemcachedCacheDriver.php index 819cf71c5c..d4aab15c92 100644 --- a/src/Core/Cache/MemcachedCacheDriver.php +++ b/src/Core/Cache/MemcachedCacheDriver.php @@ -58,7 +58,7 @@ class MemcachedCacheDriver extends AbstractCacheDriver implements IMemoryCacheDr return $this->memcached->set( $cachekey, $value, - time() + $ttl + $ttl ); } else { return $this->memcached->set( @@ -75,9 +75,13 @@ class MemcachedCacheDriver extends AbstractCacheDriver implements IMemoryCacheDr return $this->memcached->delete($cachekey); } - public function clear() + public function clear($outdated = true) { - return true; + if ($outdated) { + return true; + } else { + return $this->memcached->flush(); + } } /** diff --git a/src/Core/Cache/RedisCacheDriver.php b/src/Core/Cache/RedisCacheDriver.php index 67df8e8fee..223c2b8a94 100644 --- a/src/Core/Cache/RedisCacheDriver.php +++ b/src/Core/Cache/RedisCacheDriver.php @@ -35,15 +35,12 @@ class RedisCacheDriver extends AbstractCacheDriver implements IMemoryCacheDriver $return = null; $cachekey = $this->getCacheKey($key); - // We fetch with the hostname as key to avoid problems with other applications $cached = $this->redis->get($cachekey); - - // @see http://php.net/manual/en/redis.get.php#84275 - if (is_bool($cached) || is_double($cached) || is_long($cached)) { - return $return; + if ($cached === false && !$this->redis->exists($cachekey)) { + return null; } - $value = @unserialize($cached); + $value = json_decode($cached); // Only return a value if the serialized value is valid. // We also check if the db entry is a serialized @@ -59,44 +56,46 @@ class RedisCacheDriver extends AbstractCacheDriver implements IMemoryCacheDriver { $cachekey = $this->getCacheKey($key); - // We store with the hostname as key to avoid problems with other applications + $cached = json_encode($value); + if ($ttl > 0) { return $this->redis->setex( $cachekey, - time() + $ttl, - serialize($value) + $ttl, + $cached ); } else { return $this->redis->set( $cachekey, - serialize($value) + $cached ); } } public function delete($key) { - return $this->redis->delete($key); + $cachekey = $this->getCacheKey($key); + return ($this->redis->delete($cachekey) > 0); } - public function clear() + public function clear($outdated = true) { - return true; + if ($outdated) { + return true; + } else { + return $this->redis->flushAll(); + } } - /** * (@inheritdoc) */ public function add($key, $value, $ttl = Cache::FIVE_MINUTES) { $cachekey = $this->getCacheKey($key); + $cached = json_encode($value); - if (!is_int($value)) { - $value = serialize($value); - } - - return $this->redis->setnx($cachekey, $value); + return $this->redis->setnx($cachekey, $cached); } /** @@ -106,16 +105,14 @@ class RedisCacheDriver extends AbstractCacheDriver implements IMemoryCacheDriver { $cachekey = $this->getCacheKey($key); - if (!is_int($newValue)) { - $newValue = serialize($newValue); - } + $newCached = json_encode($newValue); $this->redis->watch($cachekey); // If the old value isn't what we expected, somebody else changed the key meanwhile - if ($this->get($cachekey) === $oldValue) { + if ($this->get($key) === $oldValue) { if ($ttl > 0) { $result = $this->redis->multi() - ->setex($cachekey, $ttl, $newValue) + ->setex($cachekey, $ttl, $newCached) ->exec(); } else { $result = $this->redis->multi() diff --git a/src/Core/Lock/CacheLockDriver.php b/src/Core/Lock/CacheLockDriver.php index ccbbb8a350..18d441ffea 100644 --- a/src/Core/Lock/CacheLockDriver.php +++ b/src/Core/Lock/CacheLockDriver.php @@ -2,6 +2,7 @@ namespace Friendica\Core\Lock; +use Friendica\Core\Cache; use Friendica\Core\Cache\IMemoryCacheDriver; class CacheLockDriver extends AbstractLockDriver @@ -24,7 +25,7 @@ class CacheLockDriver extends AbstractLockDriver /** * (@inheritdoc) */ - public function acquireLock($key, $timeout = 120) + public function acquireLock($key, $timeout = 120, $ttl = Cache::FIVE_MINUTES) { $got_lock = false; $start = time(); @@ -43,7 +44,7 @@ class CacheLockDriver extends AbstractLockDriver // At first initialize it with "0" $this->cache->add($cachekey, 0); // Now the value has to be "0" because otherwise the key was used by another process meanwhile - if ($this->cache->compareSet($cachekey, 0, getmypid(), 300)) { + if ($this->cache->compareSet($cachekey, 0, getmypid(), $ttl)) { $got_lock = true; $this->markAcquire($key); } diff --git a/src/Core/Lock/DatabaseLockDriver.php b/src/Core/Lock/DatabaseLockDriver.php index 6f4b942a4d..fc057c6b55 100644 --- a/src/Core/Lock/DatabaseLockDriver.php +++ b/src/Core/Lock/DatabaseLockDriver.php @@ -3,6 +3,7 @@ namespace Friendica\Core\Lock; use dba; +use Friendica\Core\Cache; use Friendica\Database\DBM; use Friendica\Util\DateTimeFormat; @@ -14,7 +15,7 @@ class DatabaseLockDriver extends AbstractLockDriver /** * (@inheritdoc) */ - public function acquireLock($key, $timeout = 120) + public function acquireLock($key, $timeout = 120, $ttl = Cache::FIVE_MINUTES) { $got_lock = false; $start = time(); @@ -28,16 +29,14 @@ class DatabaseLockDriver extends AbstractLockDriver // We want to lock something that was already locked by us? So we got the lock. if ($lock['pid'] == getmypid()) { $got_lock = true; - $this->markAcquire($key); } } if (!$lock['locked']) { - dba::update('locks', ['locked' => true, 'pid' => getmypid(), 'expires' => DateTimeFormat::utc('now + 300seconds')], ['name' => $key]); + dba::update('locks', ['locked' => true, 'pid' => getmypid(), 'expires' => DateTimeFormat::utc('now + ' . $ttl . 'seconds')], ['name' => $key]); $got_lock = true; - $this->markAcquire($key); } } else { - dba::insert('locks', ['name' => $key, 'locked' => true, 'pid' => getmypid(), 'expires' => DateTimeFormat::utc('now + 300seconds')]); + dba::insert('locks', ['name' => $key, 'locked' => true, 'pid' => getmypid(), 'expires' => DateTimeFormat::utc('now + ' . $ttl . 'seconds')]); $got_lock = true; $this->markAcquire($key); } diff --git a/src/Core/Lock/ILockDriver.php b/src/Core/Lock/ILockDriver.php index cd54cc03c3..a255f68345 100644 --- a/src/Core/Lock/ILockDriver.php +++ b/src/Core/Lock/ILockDriver.php @@ -1,6 +1,7 @@ cache = new ArrayCache(); + return $this->cache; + } + + public function tearDown() + { + $this->cache->clear(false); + parent::tearDown(); + } + + public function testTTL() + { + // Array Cache doesn't support TTL + return true; + } +} diff --git a/tests/src/Core/Cache/CacheTest.php b/tests/src/Core/Cache/CacheTest.php new file mode 100644 index 0000000000..4f3e4d351c --- /dev/null +++ b/tests/src/Core/Cache/CacheTest.php @@ -0,0 +1,107 @@ +instance = $this->getInstance(); + + // Reusable App object + $this->app = new App(__DIR__.'/../'); + $a = $this->app; + + // Default config + Config::set('config', 'hostname', 'localhost'); + Config::set('system', 'throttle_limit_day', 100); + Config::set('system', 'throttle_limit_week', 100); + Config::set('system', 'throttle_limit_month', 100); + Config::set('system', 'theme', 'system_theme'); + } + + function testSimple() { + $this->assertNull($this->instance->get('value1')); + + $value = 'foobar'; + $this->instance->set('value1', $value); + $received = $this->instance->get('value1'); + $this->assertEquals($value, $received, 'Value received from cache not equal to the original'); + + $value = 'ipsum lorum'; + $this->instance->set('value1', $value); + $received = $this->instance->get('value1'); + $this->assertEquals($value, $received, 'Value not overwritten by second set'); + + $value2 = 'foobar'; + $this->instance->set('value2', $value2); + $received2 = $this->instance->get('value2'); + $this->assertEquals($value, $received, 'Value changed while setting other variable'); + $this->assertEquals($value2, $received2, 'Second value not equal to original'); + + $this->assertNull($this->instance->get('not_set'), 'Unset value not equal to null'); + + $this->assertTrue($this->instance->delete('value1')); + $this->assertNull($this->instance->get('value1')); + } + + function testClear() { + $value = 'ipsum lorum'; + $this->instance->set('1_value1', $value . '1'); + $this->instance->set('1_value2', $value . '2'); + $this->instance->set('2_value1', $value . '3'); + $this->instance->set('3_value1', $value . '4'); + + $this->assertEquals([ + '1_value1' => 'ipsum lorum1', + '1_value2' => 'ipsum lorum2', + '2_value1' => 'ipsum lorum3', + '3_value1' => 'ipsum lorum4', + ], [ + '1_value1' => $this->instance->get('1_value1'), + '1_value2' => $this->instance->get('1_value2'), + '2_value1' => $this->instance->get('2_value1'), + '3_value1' => $this->instance->get('3_value1'), + ]); + + $this->assertTrue($this->instance->clear(false)); + + $this->assertEquals([ + '1_value1' => null, + '1_value2' => null, + '2_value1' => null, + '3_value1' => null, + ], [ + '1_value1' => $this->instance->get('1_value1'), + '1_value2' => $this->instance->get('1_value2'), + '2_value1' => $this->instance->get('2_value1'), + '3_value1' => $this->instance->get('3_value1'), + ]); + } + + function testTTL() { + $this->assertNull($this->instance->get('value1')); + + $value = 'foobar'; + $this->instance->set('value1', $value, 1); + $received = $this->instance->get('value1'); + $this->assertEquals($value, $received, 'Value received from cache not equal to the original'); + + sleep(2); + + $this->assertNull($this->instance->get('value1')); + } +} diff --git a/tests/src/Core/Cache/DatabaseCacheDriverTest.php b/tests/src/Core/Cache/DatabaseCacheDriverTest.php new file mode 100644 index 0000000000..5df00fc913 --- /dev/null +++ b/tests/src/Core/Cache/DatabaseCacheDriverTest.php @@ -0,0 +1,25 @@ +cache = CacheDriverFactory::create('database'); + return $this->cache; + } + + public function tearDown() + { + $this->cache->clear(false); + parent::tearDown(); + } +} diff --git a/tests/src/Core/Cache/MemcacheCacheDriverTest.php b/tests/src/Core/Cache/MemcacheCacheDriverTest.php new file mode 100644 index 0000000000..d2078236e2 --- /dev/null +++ b/tests/src/Core/Cache/MemcacheCacheDriverTest.php @@ -0,0 +1,39 @@ +cache = CacheDriverFactory::create('memcache'); + } catch (\Exception $exception) { + print "Memcache - TestCase failed: " . $exception->getMessage(); + throw new \Exception(); + } + return $this->cache; + } else { + $this->markTestSkipped('Memcache driver isn\'t available'); + return null; + } + } + + public function tearDown() + { + if (class_exists('Memcache')) { + $this->cache->clear(false); + } + parent::tearDown(); + } +} diff --git a/tests/src/Core/Cache/MemcachedCacheDriverTest.php b/tests/src/Core/Cache/MemcachedCacheDriverTest.php new file mode 100644 index 0000000000..2484517424 --- /dev/null +++ b/tests/src/Core/Cache/MemcachedCacheDriverTest.php @@ -0,0 +1,39 @@ +cache = CacheDriverFactory::create('memcached'); + } catch (\Exception $exception) { + print "Memcached - TestCase failed: " . $exception->getMessage(); + throw new \Exception(); + } + return $this->cache; + } else { + $this->markTestSkipped('Memcached driver isn\'t available'); + return null; + } + } + + public function tearDown() + { + if (class_exists('Memcached')) { + $this->cache->clear(false); + } + parent::tearDown(); + } +} diff --git a/tests/src/Core/Cache/MemoryCacheTest.php b/tests/src/Core/Cache/MemoryCacheTest.php new file mode 100644 index 0000000000..670df2fe18 --- /dev/null +++ b/tests/src/Core/Cache/MemoryCacheTest.php @@ -0,0 +1,94 @@ +instance instanceof IMemoryCacheDriver)) { + throw new \Exception('MemoryCacheTest unsupported'); + } + } + + function testCompareSet() { + $this->assertNull($this->instance->get('value1')); + + $value = 'foobar'; + $this->instance->add('value1', $value); + $received = $this->instance->get('value1'); + $this->assertEquals($value, $received, 'Value received from cache not equal to the original'); + + $newValue = 'ipsum lorum'; + $this->instance->compareSet('value1', $value, $newValue); + $received = $this->instance->get('value1'); + $this->assertEquals($newValue, $received, 'Value not overwritten by compareSet'); + } + + function testNegativeCompareSet() { + $this->assertNull($this->instance->get('value1')); + + $value = 'foobar'; + $this->instance->add('value1', $value); + $received = $this->instance->get('value1'); + $this->assertEquals($value, $received, 'Value received from cache not equal to the original'); + + $newValue = 'ipsum lorum'; + $this->instance->compareSet('value1', 'wrong', $newValue); + $received = $this->instance->get('value1'); + $this->assertNotEquals($newValue, $received, 'Value was wrongly overwritten by compareSet'); + $this->assertEquals($value, $received, 'Value was wrongly overwritten by any other value'); + } + + function testCompareDelete() { + $this->assertNull($this->instance->get('value1')); + + $value = 'foobar'; + $this->instance->add('value1', $value); + $received = $this->instance->get('value1'); + $this->assertEquals($value, $received, 'Value received from cache not equal to the original'); + $this->instance->compareDelete('value1', $value); + $this->assertNull($this->instance->get('value1'), 'Value was not deleted by compareDelete'); + } + + function testNegativeCompareDelete() { + $this->assertNull($this->instance->get('value1')); + + $value = 'foobar'; + $this->instance->add('value1', $value); + $received = $this->instance->get('value1'); + $this->assertEquals($value, $received, 'Value received from cache not equal to the original'); + $this->instance->compareDelete('value1', 'wrong'); + $this->assertNotNull($this->instance->get('value1'), 'Value was wrongly compareDeleted'); + + $this->instance->compareDelete('value1', $value); + $this->assertNull($this->instance->get('value1'), 'Value was wrongly NOT deleted by compareDelete'); + } + + function testAdd() { + $this->assertNull($this->instance->get('value1')); + + $value = 'foobar'; + $this->instance->add('value1', $value); + + $newValue = 'ipsum lorum'; + $this->instance->add('value1', $newValue); + $received = $this->instance->get('value1'); + $this->assertNotEquals($newValue, $received, 'Value was wrongly overwritten by add'); + $this->assertEquals($value, $received, 'Value was wrongly overwritten by any other value'); + + $this->instance->delete('value1'); + $this->instance->add('value1', $newValue); + $received = $this->instance->get('value1'); + $this->assertEquals($newValue, $received, 'Value was not overwritten by add'); + $this->assertNotEquals($value, $received, 'Value was not overwritten by any other value'); + } +} \ No newline at end of file diff --git a/tests/src/Core/Cache/RedisCacheDriverTest.php b/tests/src/Core/Cache/RedisCacheDriverTest.php new file mode 100644 index 0000000000..e13d95df4d --- /dev/null +++ b/tests/src/Core/Cache/RedisCacheDriverTest.php @@ -0,0 +1,39 @@ +cache = CacheDriverFactory::create('redis'); + } catch (\Exception $exception) { + print "Redis - TestCase failed: " . $exception->getMessage(); + throw new \Exception(); + } + return $this->cache; + } else { + $this->markTestSkipped('Redis driver isn\'t available'); + return null; + } + } + + public function tearDown() + { + if (class_exists('Redis')) { + $this->cache->clear(false); + } + parent::tearDown(); + } +} diff --git a/tests/src/Core/Lock/CacheLockDriverTest.php b/tests/src/Core/Lock/ArrayCacheLockDriverTest.php similarity index 74% rename from tests/src/Core/Lock/CacheLockDriverTest.php rename to tests/src/Core/Lock/ArrayCacheLockDriverTest.php index a089059725..dc044f5c5c 100644 --- a/tests/src/Core/Lock/CacheLockDriverTest.php +++ b/tests/src/Core/Lock/ArrayCacheLockDriverTest.php @@ -6,7 +6,7 @@ namespace Friendica\Test\src\Core\Lock; use Friendica\Core\Cache\ArrayCache; use Friendica\Core\Lock\CacheLockDriver; -class CacheLockDriverTest extends LockTest +class ArrayCacheLockDriverTest extends LockTest { /** * @var \Friendica\Core\Cache\IMemoryCacheDriver @@ -24,4 +24,10 @@ class CacheLockDriverTest extends LockTest $this->cache->clear(); parent::tearDown(); } -} \ No newline at end of file + + public function testLockTTL() + { + // ArrayCache doesn't support TTL + return true; + } +} diff --git a/tests/src/Core/Lock/DatabaseLockDriverTest.php b/tests/src/Core/Lock/DatabaseLockDriverTest.php index a80ff4c37c..f55ab0f9e2 100644 --- a/tests/src/Core/Lock/DatabaseLockDriverTest.php +++ b/tests/src/Core/Lock/DatabaseLockDriverTest.php @@ -11,49 +11,6 @@ use PHPUnit_Extensions_Database_DB_IDatabaseConnection; class DatabaseLockDriverTest extends LockTest { - use TestCaseTrait; - - /** - * Get database connection. - * - * This function is executed before each test in order to get a database connection that can be used by tests. - * If no prior connection is available, it tries to create one using the USER, PASS and DB environment variables. - * - * If it could not connect to the database, the test is skipped. - * - * @return PHPUnit_Extensions_Database_DB_IDatabaseConnection - * @see https://phpunit.de/manual/5.7/en/database.html - */ - protected function getConnection() - { - if (!dba::$connected) { - dba::connect('localhost', getenv('USER'), getenv('PASS'), getenv('DB')); - - if (dba::$connected) { - $app = get_app(); - // We need to do this in order to disable logging - $app->module = 'install'; - - // Create database structure - DBStructure::update(false, true, true); - } else { - $this->markTestSkipped('Could not connect to the database.'); - } - } - - return $this->createDefaultDBConnection(dba::get_db(), getenv('DB')); - } - - /** - * Get dataset to populate the database with. - * @return YamlDataSet - * @see https://phpunit.de/manual/5.7/en/database.html - */ - protected function getDataSet() - { - return new YamlDataSet(__DIR__ . '/../../../datasets/api.yml'); - } - protected function getInstance() { return new DatabaseLockDriver(); @@ -64,4 +21,4 @@ class DatabaseLockDriverTest extends LockTest dba::delete('locks', [ 'id > 0']); parent::tearDown(); } -} \ No newline at end of file +} diff --git a/tests/src/Core/Lock/LockTest.php b/tests/src/Core/Lock/LockTest.php index c8c0c32ae2..dafbd74a6f 100644 --- a/tests/src/Core/Lock/LockTest.php +++ b/tests/src/Core/Lock/LockTest.php @@ -4,9 +4,10 @@ namespace Friendica\Test\src\Core\Lock; use Friendica\App; use Friendica\Core\Config; +use Friendica\Test\DatabaseTest; use PHPUnit\Framework\TestCase; -abstract class LockTest extends TestCase +abstract class LockTest extends DatabaseTest { /** * @var \Friendica\Core\Lock\ILockDriver @@ -58,6 +59,10 @@ abstract class LockTest extends TestCase $this->instance->acquireLock('bar', 1); $this->instance->acquireLock('nice', 1); + $this->assertTrue($this->instance->isLocked('foo')); + $this->assertTrue($this->instance->isLocked('bar')); + $this->assertTrue($this->instance->isLocked('nice')); + $this->instance->releaseAll(); $this->assertFalse($this->instance->isLocked('foo')); @@ -72,9 +77,33 @@ abstract class LockTest extends TestCase $this->instance->releaseLock('foo'); + $this->assertFalse($this->instance->isLocked('foo')); + $this->assertTrue($this->instance->isLocked('bar')); + $this->assertTrue($this->instance->isLocked('nice')); + $this->instance->releaseAll(); $this->assertFalse($this->instance->isLocked('bar')); - $this->assertFalse($this->instance->isLocked('#/$%ยง')); + $this->assertFalse($this->instance->isLocked('nice')); } -} \ No newline at end of file + + function testLockTTL() { + + // TODO [nupplaphil] - Because of the Datetime-Utils for the database, we have to wait a FULL second between the checks to invalidate the db-locks/cache + $this->instance->acquireLock('foo', 1, 1); + $this->instance->acquireLock('bar', 1, 3); + + $this->assertTrue($this->instance->isLocked('foo')); + $this->assertTrue($this->instance->isLocked('bar')); + + sleep(2); + + $this->assertFalse($this->instance->isLocked('foo')); + $this->assertTrue($this->instance->isLocked('bar')); + + sleep(2); + + $this->assertFalse($this->instance->isLocked('foo')); + $this->assertFalse($this->instance->isLocked('bar')); + } +} diff --git a/tests/src/Core/Lock/MemcacheCacheLockDriverTest.php b/tests/src/Core/Lock/MemcacheCacheLockDriverTest.php new file mode 100644 index 0000000000..67ccdb57d2 --- /dev/null +++ b/tests/src/Core/Lock/MemcacheCacheLockDriverTest.php @@ -0,0 +1,40 @@ +cache = CacheDriverFactory::create('memcache'); + } catch (\Exception $exception) { + print "Memcache - TestCase failed: " . $exception->getMessage(); + throw new \Exception(); + } + return new CacheLockDriver($this->cache); + } else { + $this->markTestSkipped('Memcache driver isn\'t available'); + return null; + } + } + + public function tearDown() + { + if (class_exists('Memcache')) { + $this->cache->clear(); + } + parent::tearDown(); + } +} diff --git a/tests/src/Core/Lock/MemcachedCacheLockDriverTest.php b/tests/src/Core/Lock/MemcachedCacheLockDriverTest.php new file mode 100644 index 0000000000..e08358ce38 --- /dev/null +++ b/tests/src/Core/Lock/MemcachedCacheLockDriverTest.php @@ -0,0 +1,40 @@ +cache = CacheDriverFactory::create('memcached'); + } catch (\Exception $exception) { + print "Memcached - TestCase failed: " . $exception->getMessage(); + throw new \Exception(); + } + return new CacheLockDriver($this->cache); + } else { + $this->markTestSkipped('Memcached driver isn\'t available'); + return null; + } + } + + public function tearDown() + { + if (class_exists('Memcached')) { + $this->cache->clear(); + } + parent::tearDown(); + } +} diff --git a/tests/src/Core/Lock/RedisCacheLockDriverTest.php b/tests/src/Core/Lock/RedisCacheLockDriverTest.php new file mode 100644 index 0000000000..82d9b50def --- /dev/null +++ b/tests/src/Core/Lock/RedisCacheLockDriverTest.php @@ -0,0 +1,40 @@ +cache = CacheDriverFactory::create('redis'); + } catch (\Exception $exception) { + print "Redis - TestCase failed: " . $exception->getMessage(); + throw new \Exception(); + } + return new CacheLockDriver($this->cache); + } else { + $this->markTestSkipped('Redis driver isn\'t available'); + return null; + } + } + + public function tearDown() + { + if (class_exists('Redis')) { + $this->cache->clear(); + } + parent::tearDown(); + } +} diff --git a/tests/src/Core/Lock/SemaphoreLockDriverTest.php b/tests/src/Core/Lock/SemaphoreLockDriverTest.php index 56c96458f2..cd4b915733 100644 --- a/tests/src/Core/Lock/SemaphoreLockDriverTest.php +++ b/tests/src/Core/Lock/SemaphoreLockDriverTest.php @@ -23,4 +23,10 @@ class SemaphoreLockDriverTest extends LockTest $this->semaphoreLockDriver->releaseAll(); parent::tearDown(); } -} \ No newline at end of file + + function testLockTTL() + { + // Semaphore doesn't work with TTL + return true; + } +}