~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_lockdir.py

  • Committer: Robert Collins
  • Date: 2005-10-18 05:26:22 UTC
  • mto: This revision was merged to the branch mainline in revision 1463.
  • Revision ID: robertc@robertcollins.net-20051018052622-653d638c9e26fde4
fix broken tests

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 
174
 
        tries to lock it.
175
 
        """
176
 
        t = self.get_transport()
177
 
        lf1 = LockDir(t, 'test_lock')
178
 
        lf1.create()
179
 
        lf1.attempt_lock()
180
 
 
181
 
        def wait_and_unlock():
182
 
            time.sleep(0.1)
183
 
            lf1.unlock()
184
 
        unlocker = Thread(target=wait_and_unlock)
185
 
        unlocker.start()
186
 
        try:
187
 
            lf2 = LockDir(t, 'test_lock')
188
 
            before = time.time()
189
 
            # wait and then lock
190
 
            lf2.wait_lock(timeout=0.4, poll=0.1)
191
 
            after = time.time()
192
 
            self.assertTrue(after - before <= 1.0)
193
 
        finally:
194
 
            unlocker.join()
195
 
 
196
 
    def test_33_wait(self):
197
 
        """Succeed when waiting on a lock that gets released
198
 
 
199
 
        The difference from test_32_lock_wait_succeed is that the second 
200
 
        caller does not actually acquire the lock, but just waits for it
201
 
        to be released.  This is done over a readonly transport.
202
 
        """
203
 
        t = self.get_transport()
204
 
        lf1 = LockDir(t, 'test_lock')
205
 
        lf1.create()
206
 
        lf1.attempt_lock()
207
 
 
208
 
        def wait_and_unlock():
209
 
            time.sleep(0.1)
210
 
            lf1.unlock()
211
 
        unlocker = Thread(target=wait_and_unlock)
212
 
        unlocker.start()
213
 
        try:
214
 
            lf2 = LockDir(self.get_readonly_transport(), 'test_lock')
215
 
            before = time.time()
216
 
            # wait but don't lock
217
 
            lf2.wait(timeout=0.4, poll=0.1)
218
 
            after = time.time()
219
 
            self.assertTrue(after - before <= 1.0)
220
 
        finally:
221
 
            unlocker.join()
222
 
 
223
 
    def test_40_confirm_easy(self):
224
 
        """Confirm a lock that's already held"""
225
 
        t = self.get_transport()
226
 
        lf1 = LockDir(t, 'test_lock')
227
 
        lf1.create()
228
 
        lf1.attempt_lock()
229
 
        lf1.confirm()
230
 
 
231
 
    def test_41_confirm_not_held(self):
232
 
        """Confirm a lock that's already held"""
233
 
        t = self.get_transport()
234
 
        lf1 = LockDir(t, 'test_lock')
235
 
        lf1.create()
236
 
        self.assertRaises(LockNotHeld, lf1.confirm)
237
 
 
238
 
    def test_42_confirm_broken_manually(self):
239
 
        """Confirm a lock broken by hand"""
240
 
        t = self.get_transport()
241
 
        lf1 = LockDir(t, 'test_lock')
242
 
        lf1.create()
243
 
        lf1.attempt_lock()
244
 
        t.move('test_lock', 'lock_gone_now')
245
 
        self.assertRaises(LockBroken, lf1.confirm)
246
 
 
247
 
    def test_43_break(self):
248
 
        """Break a lock whose caller has forgotten it"""
249
 
        t = self.get_transport()
250
 
        lf1 = LockDir(t, 'test_lock')
251
 
        lf1.create()
252
 
        lf1.attempt_lock()
253
 
        # we incorrectly discard the lock object without unlocking it
254
 
        del lf1
255
 
        # someone else sees it's still locked
256
 
        lf2 = LockDir(t, 'test_lock')
257
 
        holder_info = lf2.peek()
258
 
        self.assertTrue(holder_info)
259
 
        lf2.force_break(holder_info)
260
 
        # now we should be able to take it
261
 
        lf2.attempt_lock()
262
 
        lf2.confirm()
263
 
 
264
 
    def test_44_break_already_released(self):
265
 
        """Lock break races with regular release"""
266
 
        t = self.get_transport()
267
 
        lf1 = LockDir(t, 'test_lock')
268
 
        lf1.create()
269
 
        lf1.attempt_lock()
270
 
        # someone else sees it's still locked
271
 
        lf2 = LockDir(t, 'test_lock')
272
 
        holder_info = lf2.peek()
273
 
        # in the interim the lock is released
274
 
        lf1.unlock()
275
 
        # break should succeed
276
 
        lf2.force_break(holder_info)
277
 
        # now we should be able to take it
278
 
        lf2.attempt_lock()
279
 
        lf2.confirm()
280
 
 
281
 
    def test_45_break_mismatch(self):
282
 
        """Lock break races with someone else acquiring it"""
283
 
        t = self.get_transport()
284
 
        lf1 = LockDir(t, 'test_lock')
285
 
        lf1.create()
286
 
        lf1.attempt_lock()
287
 
        # someone else sees it's still locked
288
 
        lf2 = LockDir(t, 'test_lock')
289
 
        holder_info = lf2.peek()
290
 
        # in the interim the lock is released
291
 
        lf1.unlock()
292
 
        lf3 = LockDir(t, 'test_lock')
293
 
        lf3.attempt_lock()
294
 
        # break should now *fail*
295
 
        self.assertRaises(LockBreakMismatch, lf2.force_break,
296
 
                          holder_info)
297
 
        lf3.unlock()
298
 
 
299
 
    def test_46_fake_read_lock(self):
300
 
        t = self.get_transport()
301
 
        lf1 = LockDir(t, 'test_lock')
302
 
        lf1.create()
303
 
        lf1.lock_read()
304
 
        lf1.unlock()
305
 
 
306
 
    def test_50_lockdir_representation(self):
307
 
        """Check the on-disk representation of LockDirs is as expected.
308
 
 
309
 
        There should always be a top-level directory named by the lock.
310
 
        When the lock is held, there should be a lockname/held directory 
311
 
        containing an info file.
312
 
        """
313
 
        t = self.get_transport()
314
 
        lf1 = LockDir(t, 'test_lock')
315
 
        lf1.create()
316
 
        self.assertTrue(t.has('test_lock'))
317
 
        lf1.lock_write()
318
 
        self.assertTrue(t.has('test_lock/held/info'))
319
 
        lf1.unlock()
320
 
        self.assertFalse(t.has('test_lock/held/info'))