xref: /dokuwiki/_test/tests/Search/Index/LockTest.php (revision 6734bb8cef71e8b4af23e627d4db5430304d55a2)
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