diff options
Diffstat (limited to 'tests/lib/Memcache')
-rw-r--r-- | tests/lib/Memcache/APCuTest.php | 39 | ||||
-rw-r--r-- | tests/lib/Memcache/ArrayCacheTest.php | 21 | ||||
-rw-r--r-- | tests/lib/Memcache/Cache.php | 157 | ||||
-rw-r--r-- | tests/lib/Memcache/CasTraitTest.php | 64 | ||||
-rw-r--r-- | tests/lib/Memcache/FactoryTest.php | 139 | ||||
-rw-r--r-- | tests/lib/Memcache/MemcachedTest.php | 57 | ||||
-rw-r--r-- | tests/lib/Memcache/RedisTest.php | 87 |
7 files changed, 564 insertions, 0 deletions
diff --git a/tests/lib/Memcache/APCuTest.php b/tests/lib/Memcache/APCuTest.php new file mode 100644 index 00000000000..199bdf298f6 --- /dev/null +++ b/tests/lib/Memcache/APCuTest.php @@ -0,0 +1,39 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\Memcache; + +use OC\Memcache\APCu; + +/** + * @group Memcache + * @group APCu + */ +class APCuTest extends Cache { + protected function setUp(): void { + parent::setUp(); + + if (!APCu::isAvailable()) { + $this->markTestSkipped('The APCu extension is not available.'); + return; + } + $this->instance = new APCu($this->getUniqueID()); + } + + public function testCasIntChanged(): void { + $this->instance->set('foo', 1); + $this->assertTrue($this->instance->cas('foo', 1, 2)); + $this->assertEquals(2, $this->instance->get('foo')); + } + + public function testCasIntNotChanged(): void { + $this->instance->set('foo', 1); + $this->assertFalse($this->instance->cas('foo', 2, 3)); + $this->assertEquals(1, $this->instance->get('foo')); + } +} diff --git a/tests/lib/Memcache/ArrayCacheTest.php b/tests/lib/Memcache/ArrayCacheTest.php new file mode 100644 index 00000000000..e71c821729c --- /dev/null +++ b/tests/lib/Memcache/ArrayCacheTest.php @@ -0,0 +1,21 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\Memcache; + +use OC\Memcache\ArrayCache; + +/** + * @group Memcache + */ +class ArrayCacheTest extends Cache { + protected function setUp(): void { + parent::setUp(); + $this->instance = new ArrayCache(''); + } +} diff --git a/tests/lib/Memcache/Cache.php b/tests/lib/Memcache/Cache.php new file mode 100644 index 00000000000..b48f5557faa --- /dev/null +++ b/tests/lib/Memcache/Cache.php @@ -0,0 +1,157 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\Memcache; + +use OCP\IMemcache; + +abstract class Cache extends \Test\Cache\TestCache { + /** + * @var IMemcache cache; + */ + protected $instance; + + public function testExistsAfterSet(): void { + $this->assertFalse($this->instance->hasKey('foo')); + $this->instance->set('foo', 'bar'); + $this->assertTrue($this->instance->hasKey('foo')); + } + + public function testGetAfterSet(): void { + $this->assertNull($this->instance->get('foo')); + $this->instance->set('foo', 'bar'); + $this->assertEquals('bar', $this->instance->get('foo')); + } + + public function testGetArrayAfterSet(): void { + $this->assertNull($this->instance->get('foo')); + $this->instance->set('foo', ['bar']); + $this->assertEquals(['bar'], $this->instance->get('foo')); + } + + public function testDoesNotExistAfterRemove(): void { + $this->instance->set('foo', 'bar'); + $this->instance->remove('foo'); + $this->assertFalse($this->instance->hasKey('foo')); + } + + public function testRemoveNonExisting(): void { + $this->instance->remove('foo'); + $this->assertFalse($this->instance->hasKey('foo')); + } + + public function testArrayAccessSet(): void { + $this->instance['foo'] = 'bar'; + $this->assertEquals('bar', $this->instance->get('foo')); + } + + public function testArrayAccessGet(): void { + $this->instance->set('foo', 'bar'); + $this->assertEquals('bar', $this->instance['foo']); + } + + public function testArrayAccessExists(): void { + $this->assertFalse(isset($this->instance['foo'])); + $this->instance->set('foo', 'bar'); + $this->assertTrue(isset($this->instance['foo'])); + } + + public function testArrayAccessUnset(): void { + $this->instance->set('foo', 'bar'); + unset($this->instance['foo']); + $this->assertFalse($this->instance->hasKey('foo')); + } + + public function testAdd(): void { + $this->assertTrue($this->instance->add('foo', 'bar')); + $this->assertEquals('bar', $this->instance->get('foo')); + $this->assertFalse($this->instance->add('foo', 'asd')); + $this->assertEquals('bar', $this->instance->get('foo')); + } + + public function testInc(): void { + $this->assertEquals(1, $this->instance->inc('foo')); + $this->assertEquals(1, $this->instance->get('foo')); + $this->assertEquals(2, $this->instance->inc('foo')); + $this->assertEquals(2, $this->instance->get('foo')); + $this->assertEquals(12, $this->instance->inc('foo', 10)); + $this->assertEquals(12, $this->instance->get('foo')); + + $this->instance->set('foo', 'bar'); + $this->assertFalse($this->instance->inc('foo')); + $this->assertEquals('bar', $this->instance->get('foo')); + } + + public function testDec(): void { + $this->assertFalse($this->instance->dec('foo')); + $this->instance->set('foo', 20); + $this->assertEquals(19, $this->instance->dec('foo')); + $this->assertEquals(19, $this->instance->get('foo')); + $this->assertEquals(9, $this->instance->dec('foo', 10)); + + $this->instance->set('foo', 'bar'); + $this->assertFalse($this->instance->dec('foo')); + $this->assertEquals('bar', $this->instance->get('foo')); + } + + public function testCasNotChanged(): void { + $this->instance->set('foo', 'bar'); + $this->assertTrue($this->instance->cas('foo', 'bar', 'asd')); + $this->assertEquals('asd', $this->instance->get('foo')); + } + + public function testCasChanged(): void { + $this->instance->set('foo', 'bar1'); + $this->assertFalse($this->instance->cas('foo', 'bar', 'asd')); + $this->assertEquals('bar1', $this->instance->get('foo')); + } + + public function testCasNotSet(): void { + $this->assertFalse($this->instance->cas('foo', 'bar', 'asd')); + } + + public function testCadNotChanged(): void { + $this->instance->set('foo', 'bar'); + $this->assertTrue($this->instance->cad('foo', 'bar')); + $this->assertFalse($this->instance->hasKey('foo')); + } + + public function testCadChanged(): void { + $this->instance->set('foo', 'bar1'); + $this->assertFalse($this->instance->cad('foo', 'bar')); + $this->assertTrue($this->instance->hasKey('foo')); + } + + public function testCadNotSet(): void { + $this->assertFalse($this->instance->cad('foo', 'bar')); + } + + public function testNcadNotChanged(): void { + $this->instance->set('foo', 'bar'); + $this->assertFalse($this->instance->ncad('foo', 'bar')); + $this->assertTrue($this->instance->hasKey('foo')); + } + + public function testNcadChanged(): void { + $this->instance->set('foo', 'bar1'); + $this->assertTrue($this->instance->ncad('foo', 'bar')); + $this->assertFalse($this->instance->hasKey('foo')); + } + + public function testNcadNotSet(): void { + $this->assertFalse($this->instance->ncad('foo', 'bar')); + } + + protected function tearDown(): void { + if ($this->instance) { + $this->instance->clear(); + } + + parent::tearDown(); + } +} diff --git a/tests/lib/Memcache/CasTraitTest.php b/tests/lib/Memcache/CasTraitTest.php new file mode 100644 index 00000000000..9de04fa2726 --- /dev/null +++ b/tests/lib/Memcache/CasTraitTest.php @@ -0,0 +1,64 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ + +namespace Test\Memcache; + +use OC\Memcache\ArrayCache; +use Test\TestCase; + +/** + * @group Memcache + */ +class CasTraitTest extends TestCase { + /** + * @return \OC\Memcache\CasTrait + */ + private function getCache() { + $sourceCache = new ArrayCache(); + $mock = $this->getMockForTrait('\OC\Memcache\CasTrait'); + + $mock->expects($this->any()) + ->method('set') + ->willReturnCallback(function ($key, $value, $ttl) use ($sourceCache) { + return $sourceCache->set($key, $value, $ttl); + }); + + $mock->expects($this->any()) + ->method('get') + ->willReturnCallback(function ($key) use ($sourceCache) { + return $sourceCache->get($key); + }); + + $mock->expects($this->any()) + ->method('add') + ->willReturnCallback(function ($key, $value, $ttl) use ($sourceCache) { + return $sourceCache->add($key, $value, $ttl); + }); + + $mock->expects($this->any()) + ->method('remove') + ->willReturnCallback(function ($key) use ($sourceCache) { + return $sourceCache->remove($key); + }); + return $mock; + } + + public function testCasNotChanged(): void { + $cache = $this->getCache(); + $cache->set('foo', 'bar'); + $this->assertTrue($cache->cas('foo', 'bar', 'asd')); + $this->assertEquals('asd', $cache->get('foo')); + } + + public function testCasChanged(): void { + $cache = $this->getCache(); + $cache->set('foo', 'bar1'); + $this->assertFalse($cache->cas('foo', 'bar', 'asd')); + $this->assertEquals('bar1', $cache->get('foo')); + } +} diff --git a/tests/lib/Memcache/FactoryTest.php b/tests/lib/Memcache/FactoryTest.php new file mode 100644 index 00000000000..e16e079349f --- /dev/null +++ b/tests/lib/Memcache/FactoryTest.php @@ -0,0 +1,139 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ + +namespace Test\Memcache; + +use OC\Memcache\Factory; +use OC\Memcache\NullCache; +use OCP\HintException; +use OCP\Profiler\IProfiler; +use Psr\Log\LoggerInterface; + +class Test_Factory_Available_Cache1 extends NullCache { + public function __construct($prefix = '') { + } + + public static function isAvailable(): bool { + return true; + } +} + +class Test_Factory_Available_Cache2 extends NullCache { + public function __construct($prefix = '') { + } + + public static function isAvailable(): bool { + return true; + } +} + +class Test_Factory_Unavailable_Cache1 extends NullCache { + public function __construct($prefix = '') { + } + + public static function isAvailable(): bool { + return false; + } +} + +class Test_Factory_Unavailable_Cache2 extends NullCache { + public function __construct($prefix = '') { + } + + public static function isAvailable(): bool { + return false; + } +} + +/** + * @group Memcache + */ +class FactoryTest extends \Test\TestCase { + public const AVAILABLE1 = '\\Test\\Memcache\\Test_Factory_Available_Cache1'; + public const AVAILABLE2 = '\\Test\\Memcache\\Test_Factory_Available_Cache2'; + public const UNAVAILABLE1 = '\\Test\\Memcache\\Test_Factory_Unavailable_Cache1'; + public const UNAVAILABLE2 = '\\Test\\Memcache\\Test_Factory_Unavailable_Cache2'; + + public static function cacheAvailabilityProvider(): array { + return [ + [ + // local and distributed available + self::AVAILABLE1, self::AVAILABLE2, null, + self::AVAILABLE1, self::AVAILABLE2, Factory::NULL_CACHE + ], + [ + // local and distributed null + null, null, null, + Factory::NULL_CACHE, Factory::NULL_CACHE, Factory::NULL_CACHE + ], + [ + // local available, distributed null (most common scenario) + self::AVAILABLE1, null, null, + self::AVAILABLE1, self::AVAILABLE1, Factory::NULL_CACHE + ], + [ + // locking cache available + null, null, self::AVAILABLE1, + Factory::NULL_CACHE, Factory::NULL_CACHE, self::AVAILABLE1 + ], + [ + // locking cache unavailable: no exception here in the factory + null, null, self::UNAVAILABLE1, + Factory::NULL_CACHE, Factory::NULL_CACHE, Factory::NULL_CACHE + ] + ]; + } + + public static function cacheUnavailableProvider(): array { + return [ + [ + // local available, distributed unavailable + self::AVAILABLE1, self::UNAVAILABLE1 + ], + [ + // local unavailable, distributed available + self::UNAVAILABLE1, self::AVAILABLE1 + ], + [ + // local and distributed unavailable + self::UNAVAILABLE1, self::UNAVAILABLE2 + ], + ]; + } + + #[\PHPUnit\Framework\Attributes\DataProvider('cacheAvailabilityProvider')] + public function testCacheAvailability($localCache, $distributedCache, $lockingCache, + $expectedLocalCache, $expectedDistributedCache, $expectedLockingCache): void { + $logger = $this->getMockBuilder(LoggerInterface::class)->getMock(); + $profiler = $this->getMockBuilder(IProfiler::class)->getMock(); + $factory = new Factory(fn () => 'abc', $logger, $profiler, $localCache, $distributedCache, $lockingCache); + $this->assertTrue(is_a($factory->createLocal(), $expectedLocalCache)); + $this->assertTrue(is_a($factory->createDistributed(), $expectedDistributedCache)); + $this->assertTrue(is_a($factory->createLocking(), $expectedLockingCache)); + } + + #[\PHPUnit\Framework\Attributes\DataProvider('cacheUnavailableProvider')] + public function testCacheNotAvailableException($localCache, $distributedCache): void { + $this->expectException(HintException::class); + + $logger = $this->getMockBuilder(LoggerInterface::class)->getMock(); + $profiler = $this->getMockBuilder(IProfiler::class)->getMock(); + new Factory(fn () => 'abc', $logger, $profiler, $localCache, $distributedCache); + } + + public function testCreateInMemory(): void { + $logger = $this->getMockBuilder(LoggerInterface::class)->getMock(); + $profiler = $this->getMockBuilder(IProfiler::class)->getMock(); + $factory = new Factory(fn () => 'abc', $logger, $profiler, null, null, null); + + $cache = $factory->createInMemory(); + $cache->set('test', 48); + + self::assertSame(48, $cache->get('test')); + } +} diff --git a/tests/lib/Memcache/MemcachedTest.php b/tests/lib/Memcache/MemcachedTest.php new file mode 100644 index 00000000000..61e2f42e3d6 --- /dev/null +++ b/tests/lib/Memcache/MemcachedTest.php @@ -0,0 +1,57 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\Memcache; + +use OC\Memcache\Memcached; + +/** + * @group Memcache + * @group Memcached + */ +class MemcachedTest extends Cache { + public static function setUpBeforeClass(): void { + parent::setUpBeforeClass(); + + if (!Memcached::isAvailable()) { + self::markTestSkipped('The memcached extension is not available.'); + } + $instance = new Memcached(self::getUniqueID()); + if ($instance->set(self::getUniqueID(), self::getUniqueID()) === false) { + self::markTestSkipped('memcached server seems to be down.'); + } + } + + protected function setUp(): void { + parent::setUp(); + $this->instance = new Memcached($this->getUniqueID()); + } + + public function testClear(): void { + // Memcached is sometimes broken with clear(), so we don't test it thoroughly + $value = 'ipsum lorum'; + $this->instance->set('1_value1', $value); + $this->instance->set('1_value2', $value); + $this->instance->set('2_value1', $value); + $this->instance->set('3_value1', $value); + + $this->assertTrue($this->instance->clear('1_')); + + $this->assertFalse($this->instance->hasKey('1_value1')); + $this->assertFalse($this->instance->hasKey('1_value2')); + //$this->assertTrue($this->instance->hasKey('2_value1')); + //$this->assertTrue($this->instance->hasKey('3_value1')); + + $this->assertTrue($this->instance->clear()); + + $this->assertFalse($this->instance->hasKey('1_value1')); + $this->assertFalse($this->instance->hasKey('1_value2')); + $this->assertFalse($this->instance->hasKey('2_value1')); + $this->assertFalse($this->instance->hasKey('3_value1')); + } +} diff --git a/tests/lib/Memcache/RedisTest.php b/tests/lib/Memcache/RedisTest.php new file mode 100644 index 00000000000..c1dcc954925 --- /dev/null +++ b/tests/lib/Memcache/RedisTest.php @@ -0,0 +1,87 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\Memcache; + +use OC\Memcache\Redis; +use OCP\IConfig; +use OCP\Server; + +/** + * @group Memcache + * @group Redis + */ +class RedisTest extends Cache { + /** + * @var Redis cache; + */ + protected $instance; + + public static function setUpBeforeClass(): void { + parent::setUpBeforeClass(); + + if (!Redis::isAvailable()) { + self::markTestSkipped('The redis extension is not available.'); + } + + if (Server::get(IConfig::class)->getSystemValue('redis', []) === []) { + self::markTestSkipped('Redis not configured in config.php'); + } + + $errorOccurred = false; + set_error_handler( + function ($errno, $errstr): void { + throw new \RuntimeException($errstr, 123456789); + }, + E_WARNING + ); + $instance = null; + try { + $instance = new Redis(self::getUniqueID()); + } catch (\RuntimeException $e) { + $errorOccurred = $e->getCode() === 123456789 ? $e->getMessage() : false; + } + restore_error_handler(); + if ($errorOccurred !== false) { + self::markTestSkipped($errorOccurred); + } + + if ($instance === null) { + throw new \Exception('redis server is not reachable'); + } + + if ($instance->set(self::getUniqueID(), self::getUniqueID()) === false) { + self::markTestSkipped('redis server seems to be down.'); + } + } + + protected function setUp(): void { + parent::setUp(); + $this->instance = new Redis($this->getUniqueID()); + } + + public function testScriptHashes(): void { + foreach (Redis::LUA_SCRIPTS as $script) { + $this->assertEquals(sha1($script[0]), $script[1]); + } + } + + public function testCasTtlNotChanged(): void { + $this->instance->set('foo', 'bar', 50); + $this->assertTrue($this->instance->compareSetTTL('foo', 'bar', 100)); + // allow for 1s of inaccuracy due to time moving forward + $this->assertLessThan(1, 100 - $this->instance->getTTL('foo')); + } + + public function testCasTtlChanged(): void { + $this->instance->set('foo', 'bar1', 50); + $this->assertFalse($this->instance->compareSetTTL('foo', 'bar', 100)); + // allow for 1s of inaccuracy due to time moving forward + $this->assertLessThan(1, 50 - $this->instance->getTTL('foo')); + } +} |