LockingProvider.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. <?php
  2. /**
  3. * SPDX-FileCopyrightText: 2018-2024 Nextcloud GmbH and Nextcloud contributors
  4. * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
  5. * SPDX-License-Identifier: AGPL-3.0-only
  6. */
  7. namespace Test\Lock;
  8. use OCP\Lock\ILockingProvider;
  9. use OCP\Lock\LockedException;
  10. use Test\TestCase;
  11. abstract class LockingProvider extends TestCase {
  12. /**
  13. * @var \OCP\Lock\ILockingProvider
  14. */
  15. protected $instance;
  16. /**
  17. * @return \OCP\Lock\ILockingProvider
  18. */
  19. abstract protected function getInstance();
  20. protected function setUp(): void {
  21. parent::setUp();
  22. $this->instance = $this->getInstance();
  23. }
  24. public function testExclusiveLock(): void {
  25. $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
  26. $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE));
  27. $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
  28. }
  29. public function testSharedLock(): void {
  30. $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
  31. $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE));
  32. $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
  33. }
  34. public function testDoubleSharedLock(): void {
  35. $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
  36. $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE));
  37. $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
  38. $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
  39. $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
  40. }
  41. public function testReleaseSharedLock(): void {
  42. $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
  43. $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE));
  44. $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
  45. $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
  46. $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
  47. $this->instance->releaseLock('foo', ILockingProvider::LOCK_SHARED);
  48. $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
  49. $this->instance->releaseLock('foo', ILockingProvider::LOCK_SHARED);
  50. $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
  51. }
  52. public function testDoubleExclusiveLock(): void {
  53. $this->expectException(\OCP\Lock\LockedException::class);
  54. $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
  55. $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE));
  56. $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
  57. }
  58. public function testReleaseExclusiveLock(): void {
  59. $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
  60. $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE));
  61. $this->instance->releaseLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
  62. $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE));
  63. $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
  64. }
  65. public function testExclusiveLockAfterShared(): void {
  66. $this->expectException(\OCP\Lock\LockedException::class);
  67. $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
  68. $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
  69. $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
  70. }
  71. public function testExclusiveLockAfterSharedReleased(): void {
  72. $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
  73. $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
  74. $this->instance->releaseLock('foo', ILockingProvider::LOCK_SHARED);
  75. $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
  76. $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE));
  77. }
  78. public function testReleaseAll(): void {
  79. $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
  80. $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
  81. $this->instance->acquireLock('bar', ILockingProvider::LOCK_SHARED);
  82. $this->instance->acquireLock('asd', ILockingProvider::LOCK_EXCLUSIVE);
  83. $this->instance->acquireLock('fizz#A=23', ILockingProvider::LOCK_EXCLUSIVE);
  84. $this->instance->releaseAll();
  85. $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
  86. $this->assertFalse($this->instance->isLocked('bar', ILockingProvider::LOCK_SHARED));
  87. $this->assertFalse($this->instance->isLocked('asd', ILockingProvider::LOCK_EXCLUSIVE));
  88. $this->assertFalse($this->instance->isLocked('fizz#A=23', ILockingProvider::LOCK_EXCLUSIVE));
  89. }
  90. public function testReleaseAllAfterChange(): void {
  91. $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
  92. $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
  93. $this->instance->acquireLock('bar', ILockingProvider::LOCK_SHARED);
  94. $this->instance->acquireLock('asd', ILockingProvider::LOCK_EXCLUSIVE);
  95. $this->instance->changeLock('bar', ILockingProvider::LOCK_EXCLUSIVE);
  96. $this->instance->releaseAll();
  97. $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
  98. $this->assertFalse($this->instance->isLocked('bar', ILockingProvider::LOCK_SHARED));
  99. $this->assertFalse($this->instance->isLocked('bar', ILockingProvider::LOCK_EXCLUSIVE));
  100. $this->assertFalse($this->instance->isLocked('asd', ILockingProvider::LOCK_EXCLUSIVE));
  101. }
  102. public function testReleaseAllAfterUnlock(): void {
  103. $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
  104. $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
  105. $this->instance->acquireLock('bar', ILockingProvider::LOCK_SHARED);
  106. $this->instance->acquireLock('asd', ILockingProvider::LOCK_EXCLUSIVE);
  107. $this->instance->releaseLock('bar', ILockingProvider::LOCK_SHARED);
  108. $this->instance->releaseAll();
  109. $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
  110. $this->assertFalse($this->instance->isLocked('asd', ILockingProvider::LOCK_EXCLUSIVE));
  111. }
  112. public function testReleaseAfterReleaseAll(): void {
  113. $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
  114. $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
  115. $this->instance->releaseAll();
  116. $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
  117. $this->instance->releaseLock('foo', ILockingProvider::LOCK_SHARED);
  118. }
  119. public function testSharedLockAfterExclusive(): void {
  120. $this->expectException(\OCP\Lock\LockedException::class);
  121. $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
  122. $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE));
  123. $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
  124. }
  125. public function testLockedExceptionHasPathForShared(): void {
  126. try {
  127. $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
  128. $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE));
  129. $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
  130. $this->fail('Expected locked exception');
  131. } catch (LockedException $e) {
  132. $this->assertEquals('foo', $e->getPath());
  133. }
  134. }
  135. public function testLockedExceptionHasPathForExclusive(): void {
  136. try {
  137. $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
  138. $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE));
  139. $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
  140. $this->fail('Expected locked exception');
  141. } catch (LockedException $e) {
  142. $this->assertEquals('foo', $e->getPath());
  143. }
  144. }
  145. public function testChangeLockToExclusive(): void {
  146. $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
  147. $this->instance->changeLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
  148. $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
  149. $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE));
  150. }
  151. public function testChangeLockToShared(): void {
  152. $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
  153. $this->instance->changeLock('foo', ILockingProvider::LOCK_SHARED);
  154. $this->assertFalse($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE));
  155. $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_SHARED));
  156. }
  157. public function testChangeLockToExclusiveDoubleShared(): void {
  158. $this->expectException(\OCP\Lock\LockedException::class);
  159. $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
  160. $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
  161. $this->instance->changeLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
  162. }
  163. public function testChangeLockToExclusiveNoShared(): void {
  164. $this->expectException(\OCP\Lock\LockedException::class);
  165. $this->instance->changeLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
  166. }
  167. public function testChangeLockToExclusiveFromExclusive(): void {
  168. $this->expectException(\OCP\Lock\LockedException::class);
  169. $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
  170. $this->instance->changeLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
  171. }
  172. public function testChangeLockToSharedNoExclusive(): void {
  173. $this->expectException(\OCP\Lock\LockedException::class);
  174. $this->instance->changeLock('foo', ILockingProvider::LOCK_SHARED);
  175. }
  176. public function testChangeLockToSharedFromShared(): void {
  177. $this->expectException(\OCP\Lock\LockedException::class);
  178. $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
  179. $this->instance->changeLock('foo', ILockingProvider::LOCK_SHARED);
  180. }
  181. public function testReleaseNonExistingShared(): void {
  182. $this->instance->acquireLock('foo', ILockingProvider::LOCK_SHARED);
  183. $this->instance->releaseLock('foo', ILockingProvider::LOCK_SHARED);
  184. // releasing a lock once to many should not result in a locked state
  185. $this->instance->releaseLock('foo', ILockingProvider::LOCK_SHARED);
  186. $this->instance->acquireLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
  187. $this->assertTrue($this->instance->isLocked('foo', ILockingProvider::LOCK_EXCLUSIVE));
  188. $this->instance->releaseLock('foo', ILockingProvider::LOCK_EXCLUSIVE);
  189. }
  190. }