/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

'bzr selftest' now shows a progress bar with the number of tests, and 
progress made. 'make check' shows tests in -v mode, to be more useful
for the PQM status window. (Robert Collins).

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