diff options
Diffstat (limited to 'tests/lib/Lock')
-rw-r--r-- | tests/lib/Lock/DBLockingProviderTest.php | 121 | ||||
-rw-r--r-- | tests/lib/Lock/LockingProvider.php | 251 | ||||
-rw-r--r-- | tests/lib/Lock/MemcacheLockingProviderTest.php | 37 | ||||
-rw-r--r-- | tests/lib/Lock/NonCachingDBLockingProviderTest.php | 43 |
4 files changed, 452 insertions, 0 deletions
diff --git a/tests/lib/Lock/DBLockingProviderTest.php b/tests/lib/Lock/DBLockingProviderTest.php new file mode 100644 index 00000000000..32a223b4913 --- /dev/null +++ b/tests/lib/Lock/DBLockingProviderTest.php @@ -0,0 +1,121 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ + +namespace Test\Lock; + +use OC\Lock\DBLockingProvider; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\IDBConnection; +use OCP\Lock\ILockingProvider; +use OCP\Server; + +/** + * Class DBLockingProvider + * + * @group DB + * + * @package Test\Lock + */ +class DBLockingProviderTest extends LockingProvider { + /** + * @var \OC\Lock\DBLockingProvider + */ + protected $instance; + + /** + * @var IDBConnection + */ + protected $connection; + + /** + * @var ITimeFactory + */ + protected $timeFactory; + + protected $currentTime; + + protected function setUp(): void { + $this->currentTime = time(); + $this->timeFactory = $this->createMock(ITimeFactory::class); + $this->timeFactory->expects($this->any()) + ->method('getTime') + ->willReturnCallback(function () { + return $this->currentTime; + }); + parent::setUp(); + } + + /** + * @return ILockingProvider + */ + protected function getInstance() { + $this->connection = Server::get(IDBConnection::class); + return new DBLockingProvider($this->connection, $this->timeFactory, 3600); + } + + protected function tearDown(): void { + $this->connection->executeQuery('DELETE FROM `*PREFIX*file_locks`'); + parent::tearDown(); + } + + public function testCleanEmptyLocks(): void { + $this->currentTime = 100; + $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE); + $this->instance->acquireLock('asd', ILockingProvider::LOCK_EXCLUSIVE); + + $this->currentTime = 200; + $this->instance->acquireLock('bar', ILockingProvider::LOCK_EXCLUSIVE); + $this->instance->changeLock('asd', ILockingProvider::LOCK_SHARED); + + $this->currentTime = 150 + 3600; + + $this->assertEquals(3, $this->getLockEntryCount()); + + $this->instance->cleanExpiredLocks(); + + $this->assertEquals(2, $this->getLockEntryCount()); + } + + private function getLockEntryCount() { + $query = $this->connection->prepare('SELECT count(*) FROM `*PREFIX*file_locks`'); + $query->execute(); + return $query->fetchOne(); + } + + protected function getLockValue($key) { + $query = $this->connection->getQueryBuilder(); + $query->select('lock') + ->from('file_locks') + ->where($query->expr()->eq('key', $query->createNamedParameter($key))); + + $result = $query->execute(); + $rows = $result->fetchOne(); + $result->closeCursor(); + + return $rows; + } + + public function testDoubleShared(): void { + $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED); + $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED); + + $this->assertEquals(1, $this->getLockValue('foo')); + + $this->instance->releaseLock('foo', ILockingProvider::LOCK_SHARED); + + $this->assertEquals(1, $this->getLockValue('foo')); + + $this->instance->releaseLock('foo', ILockingProvider::LOCK_SHARED); + + $this->assertEquals(1, $this->getLockValue('foo')); + + $this->instance->releaseAll(); + + $this->assertEquals(0, $this->getLockValue('foo')); + } +} diff --git a/tests/lib/Lock/LockingProvider.php b/tests/lib/Lock/LockingProvider.php new file mode 100644 index 00000000000..2827f2c9160 --- /dev/null +++ b/tests/lib/Lock/LockingProvider.php @@ -0,0 +1,251 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2018-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ + +namespace Test\Lock; + +use OCP\Lock\ILockingProvider; +use OCP\Lock\LockedException; +use Test\TestCase; + +abstract class LockingProvider extends TestCase { + /** + * @var ILockingProvider + */ + protected $instance; + + /** + * @return ILockingProvider + */ + abstract protected function getInstance(); + + protected function setUp(): void { + parent::setUp(); + $this->instance = $this->getInstance(); + } + + public function testExclusiveLock(): void { + $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE); + $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE)); + $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED)); + } + + public function testSharedLock(): void { + $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED); + $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE)); + $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED)); + } + + public function testDoubleSharedLock(): void { + $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED); + $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE)); + $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED)); + $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED); + $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED)); + } + + public function testReleaseSharedLock(): void { + $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED); + $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE)); + $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED)); + $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED); + $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED)); + $this->instance->releaseLock('foo', ILockingProvider::LOCK_SHARED); + $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED)); + $this->instance->releaseLock('foo', ILockingProvider::LOCK_SHARED); + $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED)); + } + + + public function testDoubleExclusiveLock(): void { + $this->expectException(LockedException::class); + + $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE); + $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE)); + $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE); + } + + public function testReleaseExclusiveLock(): void { + $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE); + $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE)); + $this->instance->releaseLock('foo', ILockingProvider::LOCK_EXCLUSIVE); + $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE)); + $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE); + } + + + public function testExclusiveLockAfterShared(): void { + $this->expectException(LockedException::class); + + $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED); + $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED)); + $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE); + } + + public function testExclusiveLockAfterSharedReleased(): void { + $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED); + $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED)); + $this->instance->releaseLock('foo', ILockingProvider::LOCK_SHARED); + $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE); + $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE)); + } + + public function testReleaseAll(): void { + $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED); + $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED); + $this->instance->acquireLock('bar', ILockingProvider::LOCK_SHARED); + $this->instance->acquireLock('asd', ILockingProvider::LOCK_EXCLUSIVE); + $this->instance->acquireLock('fizz#A=23', ILockingProvider::LOCK_EXCLUSIVE); + + $this->instance->releaseAll(); + + $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED)); + $this->assertFalse($this->instance->isLocked('bar', ILockingProvider::LOCK_SHARED)); + $this->assertFalse($this->instance->isLocked('asd', ILockingProvider::LOCK_EXCLUSIVE)); + $this->assertFalse($this->instance->isLocked('fizz#A=23', ILockingProvider::LOCK_EXCLUSIVE)); + } + + public function testReleaseAllAfterChange(): void { + $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED); + $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED); + $this->instance->acquireLock('bar', ILockingProvider::LOCK_SHARED); + $this->instance->acquireLock('asd', ILockingProvider::LOCK_EXCLUSIVE); + + $this->instance->changeLock('bar', ILockingProvider::LOCK_EXCLUSIVE); + + $this->instance->releaseAll(); + + $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED)); + $this->assertFalse($this->instance->isLocked('bar', ILockingProvider::LOCK_SHARED)); + $this->assertFalse($this->instance->isLocked('bar', ILockingProvider::LOCK_EXCLUSIVE)); + $this->assertFalse($this->instance->isLocked('asd', ILockingProvider::LOCK_EXCLUSIVE)); + } + + public function testReleaseAllAfterUnlock(): void { + $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED); + $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED); + $this->instance->acquireLock('bar', ILockingProvider::LOCK_SHARED); + $this->instance->acquireLock('asd', ILockingProvider::LOCK_EXCLUSIVE); + + $this->instance->releaseLock('bar', ILockingProvider::LOCK_SHARED); + + $this->instance->releaseAll(); + + $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED)); + $this->assertFalse($this->instance->isLocked('asd', ILockingProvider::LOCK_EXCLUSIVE)); + } + + public function testReleaseAfterReleaseAll(): void { + $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED); + $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED); + + $this->instance->releaseAll(); + + $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED)); + + $this->instance->releaseLock('foo', ILockingProvider::LOCK_SHARED); + } + + + + public function testSharedLockAfterExclusive(): void { + $this->expectException(LockedException::class); + + $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE); + $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE)); + $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED); + } + + public function testLockedExceptionHasPathForShared(): void { + try { + $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE); + $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE)); + $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED); + + $this->fail('Expected locked exception'); + } catch (LockedException $e) { + $this->assertEquals('foo', $e->getPath()); + } + } + + public function testLockedExceptionHasPathForExclusive(): void { + try { + $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE); + $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE)); + $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED); + + $this->fail('Expected locked exception'); + } catch (LockedException $e) { + $this->assertEquals('foo', $e->getPath()); + } + } + + public function testChangeLockToExclusive(): void { + $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED); + $this->instance->changeLock('foo', ILockingProvider::LOCK_EXCLUSIVE); + $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED)); + $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE)); + } + + public function testChangeLockToShared(): void { + $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE); + $this->instance->changeLock('foo', ILockingProvider::LOCK_SHARED); + $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE)); + $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED)); + } + + + public function testChangeLockToExclusiveDoubleShared(): void { + $this->expectException(LockedException::class); + + $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED); + $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED); + $this->instance->changeLock('foo', ILockingProvider::LOCK_EXCLUSIVE); + } + + + public function testChangeLockToExclusiveNoShared(): void { + $this->expectException(LockedException::class); + + $this->instance->changeLock('foo', ILockingProvider::LOCK_EXCLUSIVE); + } + + + public function testChangeLockToExclusiveFromExclusive(): void { + $this->expectException(LockedException::class); + + $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE); + $this->instance->changeLock('foo', ILockingProvider::LOCK_EXCLUSIVE); + } + + + public function testChangeLockToSharedNoExclusive(): void { + $this->expectException(LockedException::class); + + $this->instance->changeLock('foo', ILockingProvider::LOCK_SHARED); + } + + + public function testChangeLockToSharedFromShared(): void { + $this->expectException(LockedException::class); + + $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED); + $this->instance->changeLock('foo', ILockingProvider::LOCK_SHARED); + } + + public function testReleaseNonExistingShared(): void { + $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED); + $this->instance->releaseLock('foo', ILockingProvider::LOCK_SHARED); + + // releasing a lock once to many should not result in a locked state + $this->instance->releaseLock('foo', ILockingProvider::LOCK_SHARED); + + $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE); + $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE)); + $this->instance->releaseLock('foo', ILockingProvider::LOCK_EXCLUSIVE); + } +} diff --git a/tests/lib/Lock/MemcacheLockingProviderTest.php b/tests/lib/Lock/MemcacheLockingProviderTest.php new file mode 100644 index 00000000000..ea7b3c26b3c --- /dev/null +++ b/tests/lib/Lock/MemcacheLockingProviderTest.php @@ -0,0 +1,37 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2019-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only + */ + +namespace Test\Lock; + +use OC\Lock\MemcacheLockingProvider; +use OC\Memcache\ArrayCache; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\IMemcache; +use OCP\Lock\ILockingProvider; +use OCP\Server; + +class MemcacheLockingProviderTest extends LockingProvider { + /** + * @var IMemcache + */ + private $memcache; + + /** + * @return ILockingProvider + */ + protected function getInstance() { + $this->memcache = new ArrayCache(); + $timeProvider = Server::get(ITimeFactory::class); + return new MemcacheLockingProvider($this->memcache, $timeProvider); + } + + protected function tearDown(): void { + $this->memcache->clear(); + parent::tearDown(); + } +} diff --git a/tests/lib/Lock/NonCachingDBLockingProviderTest.php b/tests/lib/Lock/NonCachingDBLockingProviderTest.php new file mode 100644 index 00000000000..4f05f0ba892 --- /dev/null +++ b/tests/lib/Lock/NonCachingDBLockingProviderTest.php @@ -0,0 +1,43 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace Test\Lock; + +use OC\Lock\DBLockingProvider; +use OCP\IDBConnection; +use OCP\Lock\ILockingProvider; +use OCP\Server; + +/** + * @group DB + * + * @package Test\Lock + */ +class NonCachingDBLockingProviderTest extends DBLockingProviderTest { + /** + * @return ILockingProvider + */ + protected function getInstance() { + $this->connection = Server::get(IDBConnection::class); + return new DBLockingProvider($this->connection, $this->timeFactory, 3600, false); + } + + public function testDoubleShared(): void { + $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED); + $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED); + + $this->assertEquals(2, $this->getLockValue('foo')); + + $this->instance->releaseLock('foo', ILockingProvider::LOCK_SHARED); + + $this->assertEquals(1, $this->getLockValue('foo')); + + $this->instance->releaseLock('foo', ILockingProvider::LOCK_SHARED); + + $this->assertEquals(0, $this->getLockValue('foo')); + } +} |