/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_lockdir.py

Got propogation under test

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2006 Canonical Ltd
 
2
 
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
 
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
 
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
"""Tests for LockDir"""
 
18
 
 
19
from threading import Thread
 
20
import time
 
21
 
 
22
from bzrlib.errors import (
 
23
        LockBreakMismatch,
 
24
        LockContention, LockError, UnlockableTransport,
 
25
        LockNotHeld, LockBroken
 
26
        )
 
27
from bzrlib.lockdir import LockDir
 
28
from bzrlib.tests import TestCaseWithTransport
 
29
 
 
30
# These tests sometimes use threads to test the behaviour of lock files with
 
31
# concurrent actors.  This is not a typical (or necessarily supported) use;
 
32
# they're really meant for guarding between processes.
 
33
 
 
34
# These tests are run on the default transport provided by the test framework
 
35
# (typically a local disk transport).  That can be changed by the --transport
 
36
# option to bzr selftest.  The required properties of the transport
 
37
# implementation are tested separately.  (The main requirement is just that
 
38
# they don't allow overwriting nonempty directories.)
 
39
 
 
40
class TestLockDir(TestCaseWithTransport):
 
41
    """Test LockDir operations"""
 
42
 
 
43
    def test_00_lock_creation(self):
 
44
        """Creation of lock file on a transport"""
 
45
        t = self.get_transport()
 
46
        lf = LockDir(t, 'test_lock')
 
47
        self.assertFalse(lf.is_held)
 
48
 
 
49
    def test_01_lock_repr(self):
 
50
        """Lock string representation"""
 
51
        lf = LockDir(self.get_transport(), 'test_lock')
 
52
        r = repr(lf)
 
53
        self.assertContainsRe(r, r'^LockDir\(.*/test_lock\)$')
 
54
 
 
55
    def test_02_unlocked_peek(self):
 
56
        lf = LockDir(self.get_transport(), 'test_lock')
 
57
        self.assertEqual(lf.peek(), None)
 
58
 
 
59
    def test_03_readonly_peek(self):
 
60
        lf = LockDir(self.get_readonly_transport(), 'test_lock')
 
61
        self.assertEqual(lf.peek(), None)
 
62
 
 
63
    def test_10_lock_uncontested(self):
 
64
        """Acquire and release a lock"""
 
65
        t = self.get_transport()
 
66
        lf = LockDir(t, 'test_lock')
 
67
        lf.create()
 
68
        lf.attempt_lock()
 
69
        try:
 
70
            self.assertTrue(lf.is_held)
 
71
        finally:
 
72
            lf.unlock()
 
73
            self.assertFalse(lf.is_held)
 
74
 
 
75
    def test_11_create_readonly_transport(self):
 
76
        """Fail to create lock on readonly transport"""
 
77
        t = self.get_readonly_transport()
 
78
        lf = LockDir(t, 'test_lock')
 
79
        self.assertRaises(UnlockableTransport, lf.create)
 
80
 
 
81
    def test_12_lock_readonly_transport(self):
 
82
        """Fail to lock on readonly transport"""
 
83
        lf = LockDir(self.get_transport(), 'test_lock')
 
84
        lf.create()
 
85
        lf = LockDir(self.get_readonly_transport(), 'test_lock')
 
86
        self.assertRaises(UnlockableTransport, lf.attempt_lock)
 
87
 
 
88
    def test_20_lock_contested(self):
 
89
        """Contention to get a lock"""
 
90
        t = self.get_transport()
 
91
        lf1 = LockDir(t, 'test_lock')
 
92
        lf1.create()
 
93
        lf1.attempt_lock()
 
94
        lf2 = LockDir(t, 'test_lock')
 
95
        try:
 
96
            # locking is between LockDir instances; aliases within 
 
97
            # a single process are not detected
 
98
            lf2.attempt_lock()
 
99
            self.fail('Failed to detect lock collision')
 
100
        except LockContention, e:
 
101
            self.assertEqual(e.lock, lf2)
 
102
            self.assertContainsRe(str(e),
 
103
                    r'^Could not acquire.*test_lock.*$')
 
104
        lf1.unlock()
 
105
 
 
106
    def test_20_lock_peek(self):
 
107
        """Peek at the state of a lock"""
 
108
        t = self.get_transport()
 
109
        lf1 = LockDir(t, 'test_lock')
 
110
        lf1.create()
 
111
        lf1.attempt_lock()
 
112
        # lock is held, should get some info on it
 
113
        info1 = lf1.peek()
 
114
        self.assertEqual(set(info1.keys()),
 
115
                         set(['user', 'nonce', 'hostname', 'pid', 'start_time']))
 
116
        # should get the same info if we look at it through a different
 
117
        # instance
 
118
        info2 = LockDir(t, 'test_lock').peek()
 
119
        self.assertEqual(info1, info2)
 
120
        # locks which are never used should be not-held
 
121
        self.assertEqual(LockDir(t, 'other_lock').peek(), None)
 
122
 
 
123
    def test_21_peek_readonly(self):
 
124
        """Peek over a readonly transport"""
 
125
        t = self.get_transport()
 
126
        lf1 = LockDir(t, 'test_lock')
 
127
        lf1.create()
 
128
        lf2 = LockDir(self.get_readonly_transport(), 'test_lock')
 
129
        self.assertEqual(lf2.peek(), None)
 
130
        lf1.attempt_lock()
 
131
        info2 = lf2.peek()
 
132
        self.assertTrue(info2)
 
133
        self.assertEqual(info2['nonce'], lf1.nonce)
 
134
 
 
135
    def test_30_lock_wait_fail(self):
 
136
        """Wait on a lock, then fail
 
137
        
 
138
        We ask to wait up to 400ms; this should fail within at most one
 
139
        second.  (Longer times are more realistic but we don't want the test
 
140
        suite to take too long, and this should do for now.)
 
141
        """
 
142
        t = self.get_transport()
 
143
        lf1 = LockDir(t, 'test_lock')
 
144
        lf1.create()
 
145
        lf2 = LockDir(t, 'test_lock')
 
146
        lf1.attempt_lock()
 
147
        try:
 
148
            before = time.time()
 
149
            self.assertRaises(LockContention, lf2.wait_lock,
 
150
                              timeout=0.4, poll=0.1)
 
151
            after = time.time()
 
152
            self.assertTrue(after - before <= 1.0)
 
153
        finally:
 
154
            lf1.unlock()
 
155
 
 
156
    def test_31_lock_wait_easy(self):
 
157
        """Succeed when waiting on a lock with no contention.
 
158
        """
 
159
        t = self.get_transport()
 
160
        lf1 = LockDir(t, 'test_lock')
 
161
        lf1.create()
 
162
        try:
 
163
            before = time.time()
 
164
            lf1.wait_lock(timeout=0.4, poll=0.1)
 
165
            after = time.time()
 
166
            self.assertTrue(after - before <= 1.0)
 
167
        finally:
 
168
            lf1.unlock()
 
169
 
 
170
    def test_32_lock_wait_succeed(self):
 
171
        """Succeed when trying to acquire a lock that gets released
 
172
 
 
173
        One thread holds on a lock and then releases it; another tries to lock it.
 
174
        """
 
175
        t = self.get_transport()
 
176
        lf1 = LockDir(t, 'test_lock')
 
177
        lf1.create()
 
178
        lf1.attempt_lock()
 
179
 
 
180
        def wait_and_unlock():
 
181
            time.sleep(0.1)
 
182
            lf1.unlock()
 
183
        unlocker = Thread(target=wait_and_unlock)
 
184
        unlocker.start()
 
185
        try:
 
186
            lf2 = LockDir(t, 'test_lock')
 
187
            before = time.time()
 
188
            # wait and then lock
 
189
            lf2.wait_lock(timeout=0.4, poll=0.1)
 
190
            after = time.time()
 
191
            self.assertTrue(after - before <= 1.0)
 
192
        finally:
 
193
            unlocker.join()
 
194
 
 
195
    def test_33_wait(self):
 
196
        """Succeed when waiting on a lock that gets released
 
197
 
 
198
        The difference from test_32_lock_wait_succeed is that the second 
 
199
        caller does not actually acquire the lock, but just waits for it
 
200
        to be released.  This is done over a readonly transport.
 
201
        """
 
202
        t = self.get_transport()
 
203
        lf1 = LockDir(t, 'test_lock')
 
204
        lf1.create()
 
205
        lf1.attempt_lock()
 
206
 
 
207
        def wait_and_unlock():
 
208
            time.sleep(0.1)
 
209
            lf1.unlock()
 
210
        unlocker = Thread(target=wait_and_unlock)
 
211
        unlocker.start()
 
212
        try:
 
213
            lf2 = LockDir(self.get_readonly_transport(), 'test_lock')
 
214
            before = time.time()
 
215
            # wait but don't lock
 
216
            lf2.wait(timeout=0.4, poll=0.1)
 
217
            after = time.time()
 
218
            self.assertTrue(after - before <= 1.0)
 
219
        finally:
 
220
            unlocker.join()
 
221
 
 
222
    def test_40_confirm_easy(self):
 
223
        """Confirm a lock that's already held"""
 
224
        t = self.get_transport()
 
225
        lf1 = LockDir(t, 'test_lock')
 
226
        lf1.create()
 
227
        lf1.attempt_lock()
 
228
        lf1.confirm()
 
229
 
 
230
    def test_41_confirm_not_held(self):
 
231
        """Confirm a lock that's already held"""
 
232
        t = self.get_transport()
 
233
        lf1 = LockDir(t, 'test_lock')
 
234
        lf1.create()
 
235
        self.assertRaises(LockNotHeld, lf1.confirm)
 
236
 
 
237
    def test_42_confirm_broken_manually(self):
 
238
        """Confirm a lock broken by hand"""
 
239
        t = self.get_transport()
 
240
        lf1 = LockDir(t, 'test_lock')
 
241
        lf1.create()
 
242
        lf1.attempt_lock()
 
243
        t.move('test_lock', 'lock_gone_now')
 
244
        self.assertRaises(LockBroken, lf1.confirm)
 
245
 
 
246
    def test_43_break(self):
 
247
        """Break a lock whose caller has forgotten it"""
 
248
        t = self.get_transport()
 
249
        lf1 = LockDir(t, 'test_lock')
 
250
        lf1.create()
 
251
        lf1.attempt_lock()
 
252
        # we incorrectly discard the lock object without unlocking it
 
253
        del lf1
 
254
        # someone else sees it's still locked
 
255
        lf2 = LockDir(t, 'test_lock')
 
256
        holder_info = lf2.peek()
 
257
        self.assertTrue(holder_info)
 
258
        lf2.force_break(holder_info)
 
259
        # now we should be able to take it
 
260
        lf2.attempt_lock()
 
261
        lf2.confirm()
 
262
 
 
263
    def test_44_break_already_released(self):
 
264
        """Lock break races with regular release"""
 
265
        t = self.get_transport()
 
266
        lf1 = LockDir(t, 'test_lock')
 
267
        lf1.create()
 
268
        lf1.attempt_lock()
 
269
        # someone else sees it's still locked
 
270
        lf2 = LockDir(t, 'test_lock')
 
271
        holder_info = lf2.peek()
 
272
        # in the interim the lock is released
 
273
        lf1.unlock()
 
274
        # break should succeed
 
275
        lf2.force_break(holder_info)
 
276
        # now we should be able to take it
 
277
        lf2.attempt_lock()
 
278
        lf2.confirm()
 
279
 
 
280
    def test_45_break_mismatch(self):
 
281
        """Lock break races with someone else acquiring it"""
 
282
        t = self.get_transport()
 
283
        lf1 = LockDir(t, 'test_lock')
 
284
        lf1.create()
 
285
        lf1.attempt_lock()
 
286
        # someone else sees it's still locked
 
287
        lf2 = LockDir(t, 'test_lock')
 
288
        holder_info = lf2.peek()
 
289
        # in the interim the lock is released
 
290
        lf1.unlock()
 
291
        lf3 = LockDir(t, 'test_lock')
 
292
        lf3.attempt_lock()
 
293
        # break should now *fail*
 
294
        self.assertRaises(LockBreakMismatch, lf2.force_break,
 
295
                          holder_info)
 
296
        lf3.unlock()
 
297
 
 
298
    def test_46_fake_read_lock(self):
 
299
        t = self.get_transport()
 
300
        lf1 = LockDir(t, 'test_lock')
 
301
        lf1.create()
 
302
        lf1.lock_read()
 
303
        lf1.unlock()
 
304
 
 
305
    def test_50_lockdir_representation(self):
 
306
        """Check the on-disk representation of LockDirs is as expected.
 
307
 
 
308
        There should always be a top-level directory named by the lock.
 
309
        When the lock is held, there should be a lockname/held directory 
 
310
        containing an info file.
 
311
        """
 
312
        t = self.get_transport()
 
313
        lf1 = LockDir(t, 'test_lock')
 
314
        lf1.create()
 
315
        self.assertTrue(t.has('test_lock'))
 
316
        lf1.lock_write()
 
317
        self.assertTrue(t.has('test_lock/held/info'))
 
318
        lf1.unlock()
 
319
        self.assertFalse(t.has('test_lock/held/info'))