1<?php 2 3namespace dokuwiki\test\Search\Index; 4 5use dokuwiki\Search\Exception\IndexLockException; 6use dokuwiki\Search\Index\Lock; 7 8class LockTest extends \DokuWikiTest 9{ 10 protected function tearDown(): void 11 { 12 Lock::releaseAll(); 13 parent::tearDown(); 14 } 15 16 public function testAcquireAndRelease() 17 { 18 Lock::acquire('test_lock'); 19 20 // lock directory should exist 21 global $conf; 22 $this->assertDirectoryExists($conf['lockdir'] . 'test_lock.index'); 23 24 Lock::release('test_lock'); 25 26 // lock directory should be removed 27 $this->assertDirectoryDoesNotExist($conf['lockdir'] . 'test_lock.index'); 28 } 29 30 public function testReferenceCounting() 31 { 32 global $conf; 33 $dir = $conf['lockdir'] . 'refcount.index'; 34 35 Lock::acquire('refcount'); 36 Lock::acquire('refcount'); 37 $this->assertDirectoryExists($dir); 38 39 // first release only decrements, lock stays 40 Lock::release('refcount'); 41 $this->assertDirectoryExists($dir); 42 43 // second release removes the lock 44 Lock::release('refcount'); 45 $this->assertDirectoryDoesNotExist($dir); 46 } 47 48 public function testReleaseUnheldLockIsNoop() 49 { 50 // should not throw or error 51 Lock::release('never_acquired'); 52 $this->assertTrue(true); 53 } 54 55 public function testAcquireFailsWhenAlreadyLockedByAnotherProcess() 56 { 57 global $conf; 58 $dir = $conf['lockdir'] . 'foreign.index'; 59 60 // simulate a foreign lock by creating the directory directly 61 mkdir($dir); 62 // set mtime to now so it's not stale 63 touch($dir); 64 65 $this->expectException(IndexLockException::class); 66 Lock::acquire('foreign'); 67 } 68 69 public function testStaleLockIsOverridden() 70 { 71 global $conf; 72 $dir = $conf['lockdir'] . 'stale.index'; 73 74 // simulate a stale lock (older than 5 minutes) 75 mkdir($dir); 76 touch($dir, time() - 400); 77 78 // should succeed by removing the stale lock 79 Lock::acquire('stale'); 80 $this->assertDirectoryExists($dir); 81 82 Lock::release('stale'); 83 } 84 85 public function testReleaseAll() 86 { 87 global $conf; 88 89 Lock::acquire('all_a'); 90 Lock::acquire('all_b'); 91 Lock::acquire('all_a'); // refcount 2 92 93 Lock::releaseAll(); 94 95 $this->assertDirectoryDoesNotExist($conf['lockdir'] . 'all_a.index'); 96 $this->assertDirectoryDoesNotExist($conf['lockdir'] . 'all_b.index'); 97 98 // releasing after releaseAll should be safe 99 Lock::release('all_a'); 100 } 101 102 public function testMultipleIndependentLocks() 103 { 104 global $conf; 105 106 Lock::acquire('ind_a'); 107 Lock::acquire('ind_b'); 108 109 $this->assertDirectoryExists($conf['lockdir'] . 'ind_a.index'); 110 $this->assertDirectoryExists($conf['lockdir'] . 'ind_b.index'); 111 112 Lock::release('ind_a'); 113 $this->assertDirectoryDoesNotExist($conf['lockdir'] . 'ind_a.index'); 114 $this->assertDirectoryExists($conf['lockdir'] . 'ind_b.index'); 115 116 Lock::release('ind_b'); 117 $this->assertDirectoryDoesNotExist($conf['lockdir'] . 'ind_b.index'); 118 } 119} 120