/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2872.5.1 by Martin Pool
Avoid internal error tracebacks on failure to lock on readonly transport (#129701).
1
# Copyright (C) 2006, 2007 Canonical Ltd
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
2
#
1553.5.12 by Martin Pool
New LockDir locking mechanism
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
7
#
1553.5.12 by Martin Pool
New LockDir locking mechanism
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.
1887.1.1 by Adeodato Simó
Do not separate paragraphs in the copyright statement with blank lines,
12
#
1553.5.12 by Martin Pool
New LockDir locking mechanism
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
1687.1.5 by Robert Collins
Add break_lock utility function to LockDir.
19
from cStringIO import StringIO
1551.10.3 by Aaron Bentley
Lock attempts don't treat permission problems as lock contention
20
import os
1957.1.7 by John Arbash Meinel
Add the ability to report if the lock changes from underneath you
21
from threading import Thread, Lock
1553.5.12 by Martin Pool
New LockDir locking mechanism
22
import time
23
1687.1.5 by Robert Collins
Add break_lock utility function to LockDir.
24
import bzrlib
1957.1.7 by John Arbash Meinel
Add the ability to report if the lock changes from underneath you
25
from bzrlib import (
2055.2.1 by John Arbash Meinel
Make LockDir less sensitive to invalid configuration of email
26
    config,
1551.10.3 by Aaron Bentley
Lock attempts don't treat permission problems as lock contention
27
    errors,
1957.1.7 by John Arbash Meinel
Add the ability to report if the lock changes from underneath you
28
    osutils,
1551.10.4 by Aaron Bentley
Update to skip on win32
29
    tests,
2555.3.9 by Martin Pool
Add test and fix for locking robustly when rename of directories doesn't act as a mutex (thank John)
30
    transport,
1957.1.7 by John Arbash Meinel
Add the ability to report if the lock changes from underneath you
31
    )
1553.5.27 by Martin Pool
Confirm that only the intended holder of a lock was broken.
32
from bzrlib.errors import (
2872.5.1 by Martin Pool
Avoid internal error tracebacks on failure to lock on readonly transport (#129701).
33
    LockBreakMismatch,
34
    LockBroken,
35
    LockContention,
36
    LockError,
37
    LockFailed,
38
    LockNotHeld,
39
    UnlockableTransport,
40
    )
2381.1.4 by Robert Collins
Unbreak lockdir tests after adding fast lockdir timeouts to the test suite default environment.
41
from bzrlib.lockdir import LockDir
1553.5.33 by Martin Pool
LockDir review comment fixes
42
from bzrlib.tests import TestCaseWithTransport
1957.1.7 by John Arbash Meinel
Add the ability to report if the lock changes from underneath you
43
from bzrlib.trace import note
1553.5.12 by Martin Pool
New LockDir locking mechanism
44
45
# These tests sometimes use threads to test the behaviour of lock files with
46
# concurrent actors.  This is not a typical (or necessarily supported) use;
47
# they're really meant for guarding between processes.
48
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
49
# These tests are run on the default transport provided by the test framework
50
# (typically a local disk transport).  That can be changed by the --transport
51
# option to bzr selftest.  The required properties of the transport
52
# implementation are tested separately.  (The main requirement is just that
53
# they don't allow overwriting nonempty directories.)
54
1553.5.12 by Martin Pool
New LockDir locking mechanism
55
class TestLockDir(TestCaseWithTransport):
56
    """Test LockDir operations"""
57
1957.1.1 by John Arbash Meinel
Report to the user when we are spinning on a lock
58
    def logging_report_function(self, fmt, *args):
59
        self._logged_reports.append((fmt, args))
60
61
    def setup_log_reporter(self, lock_dir):
62
        self._logged_reports = []
63
        lock_dir._report_function = self.logging_report_function
64
1553.5.12 by Martin Pool
New LockDir locking mechanism
65
    def test_00_lock_creation(self):
66
        """Creation of lock file on a transport"""
67
        t = self.get_transport()
68
        lf = LockDir(t, 'test_lock')
1553.5.13 by Martin Pool
New Transport.rename that mustn't overwrite
69
        self.assertFalse(lf.is_held)
1553.5.12 by Martin Pool
New LockDir locking mechanism
70
71
    def test_01_lock_repr(self):
72
        """Lock string representation"""
73
        lf = LockDir(self.get_transport(), 'test_lock')
74
        r = repr(lf)
75
        self.assertContainsRe(r, r'^LockDir\(.*/test_lock\)$')
76
77
    def test_02_unlocked_peek(self):
78
        lf = LockDir(self.get_transport(), 'test_lock')
79
        self.assertEqual(lf.peek(), None)
80
1687.1.3 by Robert Collins
Make LockDir.unlock check the lock is still intact.
81
    def get_lock(self):
82
        return LockDir(self.get_transport(), 'test_lock')
83
84
    def test_unlock_after_break_raises(self):
85
        ld = self.get_lock()
86
        ld2 = self.get_lock()
87
        ld.create()
88
        ld.attempt_lock()
89
        ld2.force_break(ld2.peek())
90
        self.assertRaises(LockBroken, ld.unlock)
91
1553.5.12 by Martin Pool
New LockDir locking mechanism
92
    def test_03_readonly_peek(self):
93
        lf = LockDir(self.get_readonly_transport(), 'test_lock')
94
        self.assertEqual(lf.peek(), None)
95
96
    def test_10_lock_uncontested(self):
97
        """Acquire and release a lock"""
98
        t = self.get_transport()
99
        lf = LockDir(t, 'test_lock')
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
100
        lf.create()
1553.5.12 by Martin Pool
New LockDir locking mechanism
101
        lf.attempt_lock()
102
        try:
1553.5.13 by Martin Pool
New Transport.rename that mustn't overwrite
103
            self.assertTrue(lf.is_held)
1553.5.12 by Martin Pool
New LockDir locking mechanism
104
        finally:
105
            lf.unlock()
1553.5.13 by Martin Pool
New Transport.rename that mustn't overwrite
106
            self.assertFalse(lf.is_held)
1553.5.12 by Martin Pool
New LockDir locking mechanism
107
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
108
    def test_11_create_readonly_transport(self):
109
        """Fail to create lock on readonly transport"""
110
        t = self.get_readonly_transport()
111
        lf = LockDir(t, 'test_lock')
2872.5.1 by Martin Pool
Avoid internal error tracebacks on failure to lock on readonly transport (#129701).
112
        self.assertRaises(LockFailed, lf.create)
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
113
114
    def test_12_lock_readonly_transport(self):
1553.5.12 by Martin Pool
New LockDir locking mechanism
115
        """Fail to lock on readonly transport"""
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
116
        lf = LockDir(self.get_transport(), 'test_lock')
117
        lf.create()
118
        lf = LockDir(self.get_readonly_transport(), 'test_lock')
2872.5.1 by Martin Pool
Avoid internal error tracebacks on failure to lock on readonly transport (#129701).
119
        self.assertRaises(LockFailed, lf.attempt_lock)
1553.5.12 by Martin Pool
New LockDir locking mechanism
120
121
    def test_20_lock_contested(self):
122
        """Contention to get a lock"""
123
        t = self.get_transport()
124
        lf1 = LockDir(t, 'test_lock')
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
125
        lf1.create()
1553.5.12 by Martin Pool
New LockDir locking mechanism
126
        lf1.attempt_lock()
127
        lf2 = LockDir(t, 'test_lock')
128
        try:
129
            # locking is between LockDir instances; aliases within 
130
            # a single process are not detected
131
            lf2.attempt_lock()
132
            self.fail('Failed to detect lock collision')
133
        except LockContention, e:
134
            self.assertEqual(e.lock, lf2)
135
            self.assertContainsRe(str(e),
136
                    r'^Could not acquire.*test_lock.*$')
137
        lf1.unlock()
138
139
    def test_20_lock_peek(self):
140
        """Peek at the state of a lock"""
141
        t = self.get_transport()
142
        lf1 = LockDir(t, 'test_lock')
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
143
        lf1.create()
1553.5.12 by Martin Pool
New LockDir locking mechanism
144
        lf1.attempt_lock()
145
        # lock is held, should get some info on it
146
        info1 = lf1.peek()
147
        self.assertEqual(set(info1.keys()),
148
                         set(['user', 'nonce', 'hostname', 'pid', 'start_time']))
149
        # should get the same info if we look at it through a different
150
        # instance
151
        info2 = LockDir(t, 'test_lock').peek()
152
        self.assertEqual(info1, info2)
153
        # locks which are never used should be not-held
154
        self.assertEqual(LockDir(t, 'other_lock').peek(), None)
155
156
    def test_21_peek_readonly(self):
157
        """Peek over a readonly transport"""
158
        t = self.get_transport()
159
        lf1 = LockDir(t, 'test_lock')
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
160
        lf1.create()
1553.5.12 by Martin Pool
New LockDir locking mechanism
161
        lf2 = LockDir(self.get_readonly_transport(), 'test_lock')
162
        self.assertEqual(lf2.peek(), None)
163
        lf1.attempt_lock()
164
        info2 = lf2.peek()
165
        self.assertTrue(info2)
166
        self.assertEqual(info2['nonce'], lf1.nonce)
167
168
    def test_30_lock_wait_fail(self):
169
        """Wait on a lock, then fail
170
        
171
        We ask to wait up to 400ms; this should fail within at most one
172
        second.  (Longer times are more realistic but we don't want the test
173
        suite to take too long, and this should do for now.)
174
        """
175
        t = self.get_transport()
176
        lf1 = LockDir(t, 'test_lock')
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
177
        lf1.create()
1553.5.12 by Martin Pool
New LockDir locking mechanism
178
        lf2 = LockDir(t, 'test_lock')
1957.1.1 by John Arbash Meinel
Report to the user when we are spinning on a lock
179
        self.setup_log_reporter(lf2)
1553.5.12 by Martin Pool
New LockDir locking mechanism
180
        lf1.attempt_lock()
181
        try:
182
            before = time.time()
183
            self.assertRaises(LockContention, lf2.wait_lock,
184
                              timeout=0.4, poll=0.1)
185
            after = time.time()
1704.2.1 by Martin Pool
Fix time-dependency in LockDir tests -- allow more margin for error in time to detect lock contention
186
            # it should only take about 0.4 seconds, but we allow more time in
187
            # case the machine is heavily loaded
188
            self.assertTrue(after - before <= 8.0, 
189
                    "took %f seconds to detect lock contention" % (after - before))
1553.5.12 by Martin Pool
New LockDir locking mechanism
190
        finally:
191
            lf1.unlock()
1957.1.1 by John Arbash Meinel
Report to the user when we are spinning on a lock
192
        lock_base = lf2.transport.abspath(lf2.path)
1957.1.7 by John Arbash Meinel
Add the ability to report if the lock changes from underneath you
193
        self.assertEqual(1, len(self._logged_reports))
1957.1.9 by John Arbash Meinel
Change default timeouts, and report differently the first failure
194
        self.assertEqual('%s %s\n'
1957.1.7 by John Arbash Meinel
Add the ability to report if the lock changes from underneath you
195
                         '%s\n%s\n'
1957.1.13 by John Arbash Meinel
Change to reporting the time when we will stop trying to grab the lock
196
                         'Will continue to try until %s\n',
1957.1.7 by John Arbash Meinel
Add the ability to report if the lock changes from underneath you
197
                         self._logged_reports[0][0])
198
        args = self._logged_reports[0][1]
1957.1.9 by John Arbash Meinel
Change default timeouts, and report differently the first failure
199
        self.assertEqual('Unable to obtain', args[0])
200
        self.assertEqual('lock %s' % (lock_base,), args[1])
201
        self.assertStartsWith(args[2], 'held by ')
202
        self.assertStartsWith(args[3], 'locked ')
203
        self.assertEndsWith(args[3], ' ago')
1957.1.13 by John Arbash Meinel
Change to reporting the time when we will stop trying to grab the lock
204
        self.assertContainsRe(args[4], r'\d\d:\d\d:\d\d')
1553.5.12 by Martin Pool
New LockDir locking mechanism
205
206
    def test_31_lock_wait_easy(self):
207
        """Succeed when waiting on a lock with no contention.
208
        """
209
        t = self.get_transport()
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
210
        lf1 = LockDir(t, 'test_lock')
211
        lf1.create()
1957.1.1 by John Arbash Meinel
Report to the user when we are spinning on a lock
212
        self.setup_log_reporter(lf1)
1553.5.12 by Martin Pool
New LockDir locking mechanism
213
        try:
214
            before = time.time()
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
215
            lf1.wait_lock(timeout=0.4, poll=0.1)
1553.5.12 by Martin Pool
New LockDir locking mechanism
216
            after = time.time()
217
            self.assertTrue(after - before <= 1.0)
218
        finally:
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
219
            lf1.unlock()
1957.1.1 by John Arbash Meinel
Report to the user when we are spinning on a lock
220
        self.assertEqual([], self._logged_reports)
1553.5.12 by Martin Pool
New LockDir locking mechanism
221
1551.15.18 by Aaron Bentley
Skip itermittently-failing test instead of deleting it
222
    def test_32_lock_wait_succeed(self):
223
        """Succeed when trying to acquire a lock that gets released
224
225
        One thread holds on a lock and then releases it; another 
226
        tries to lock it.
227
        """
1551.15.22 by Aaron Bentley
Redo test skip
228
        # This test sometimes fails like this:
229
        # Traceback (most recent call last):
230
231
        #   File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/tests/
232
        # test_lockdir.py", line 247, in test_32_lock_wait_succeed
233
        #     self.assertEqual(1, len(self._logged_reports))
234
        # AssertionError: not equal:
235
        # a = 1
236
        # b = 0
237
        raise tests.TestSkipped("Test fails intermittently")
1551.15.18 by Aaron Bentley
Skip itermittently-failing test instead of deleting it
238
        t = self.get_transport()
239
        lf1 = LockDir(t, 'test_lock')
240
        lf1.create()
241
        lf1.attempt_lock()
242
243
        def wait_and_unlock():
244
            time.sleep(0.1)
245
            lf1.unlock()
246
        unlocker = Thread(target=wait_and_unlock)
247
        unlocker.start()
248
        try:
249
            lf2 = LockDir(t, 'test_lock')
250
            self.setup_log_reporter(lf2)
251
            before = time.time()
252
            # wait and then lock
253
            lf2.wait_lock(timeout=0.4, poll=0.1)
254
            after = time.time()
255
            self.assertTrue(after - before <= 1.0)
256
        finally:
257
            unlocker.join()
258
259
        # There should be only 1 report, even though it should have to
260
        # wait for a while
261
        lock_base = lf2.transport.abspath(lf2.path)
262
        self.assertEqual(1, len(self._logged_reports))
263
        self.assertEqual('%s %s\n'
264
                         '%s\n%s\n'
265
                         'Will continue to try until %s\n',
266
                         self._logged_reports[0][0])
267
        args = self._logged_reports[0][1]
268
        self.assertEqual('Unable to obtain', args[0])
269
        self.assertEqual('lock %s' % (lock_base,), args[1])
270
        self.assertStartsWith(args[2], 'held by ')
271
        self.assertStartsWith(args[3], 'locked ')
272
        self.assertEndsWith(args[3], ' ago')
273
        self.assertContainsRe(args[4], r'\d\d:\d\d:\d\d')
274
1957.1.2 by John Arbash Meinel
Switch the default from instantly aborting, to waiting as long as 1 minute (down from 5 minutes)
275
    def test_34_lock_write_waits(self):
276
        """LockDir.lock_write() will wait for the lock.""" 
2381.1.4 by Robert Collins
Unbreak lockdir tests after adding fast lockdir timeouts to the test suite default environment.
277
        # the test suite sets the default to 0 to make deadlocks fail fast.
278
        # change it for this test, as we want to try a manual deadlock.
2631.1.4 by Aaron Bentley
Fix test skip
279
        raise tests.TestSkipped('Timing-sensitive test')
2381.1.4 by Robert Collins
Unbreak lockdir tests after adding fast lockdir timeouts to the test suite default environment.
280
        bzrlib.lockdir._DEFAULT_TIMEOUT_SECONDS = 300
1957.1.2 by John Arbash Meinel
Switch the default from instantly aborting, to waiting as long as 1 minute (down from 5 minutes)
281
        t = self.get_transport()
282
        lf1 = LockDir(t, 'test_lock')
283
        lf1.create()
284
        lf1.attempt_lock()
285
286
        def wait_and_unlock():
287
            time.sleep(0.1)
288
            lf1.unlock()
289
        unlocker = Thread(target=wait_and_unlock)
290
        unlocker.start()
291
        try:
292
            lf2 = LockDir(t, 'test_lock')
293
            self.setup_log_reporter(lf2)
294
            before = time.time()
295
            # wait and then lock
296
            lf2.lock_write()
297
            after = time.time()
298
        finally:
299
            unlocker.join()
300
301
        # There should be only 1 report, even though it should have to
302
        # wait for a while
303
        lock_base = lf2.transport.abspath(lf2.path)
1957.1.7 by John Arbash Meinel
Add the ability to report if the lock changes from underneath you
304
        self.assertEqual(1, len(self._logged_reports))
1957.1.9 by John Arbash Meinel
Change default timeouts, and report differently the first failure
305
        self.assertEqual('%s %s\n'
1957.1.7 by John Arbash Meinel
Add the ability to report if the lock changes from underneath you
306
                         '%s\n%s\n'
1957.1.13 by John Arbash Meinel
Change to reporting the time when we will stop trying to grab the lock
307
                         'Will continue to try until %s\n',
1957.1.7 by John Arbash Meinel
Add the ability to report if the lock changes from underneath you
308
                         self._logged_reports[0][0])
309
        args = self._logged_reports[0][1]
1957.1.9 by John Arbash Meinel
Change default timeouts, and report differently the first failure
310
        self.assertEqual('Unable to obtain', args[0])
311
        self.assertEqual('lock %s' % (lock_base,), args[1])
312
        self.assertStartsWith(args[2], 'held by ')
313
        self.assertStartsWith(args[3], 'locked ')
314
        self.assertEndsWith(args[3], ' ago')
1957.1.13 by John Arbash Meinel
Change to reporting the time when we will stop trying to grab the lock
315
        self.assertContainsRe(args[4], r'\d\d:\d\d:\d\d')
1957.1.7 by John Arbash Meinel
Add the ability to report if the lock changes from underneath you
316
317
    def test_35_wait_lock_changing(self):
318
        """LockDir.wait_lock() will report if the lock changes underneath.
319
        
320
        This is the stages we want to happen:
321
322
        0) Synchronization locks are created and locked.
323
        1) Lock1 obtains the lockdir, and releases the 'check' lock.
324
        2) Lock2 grabs the 'check' lock, and checks the lockdir.
325
           It sees the lockdir is already acquired, reports the fact, 
1957.1.11 by John Arbash Meinel
Switch from locking the peek() to locking attempt_lock(), which is much more stable
326
           and unsets the 'checked' lock.
327
        3) Thread1 blocks on acquiring the 'checked' lock, and then tells
1957.1.7 by John Arbash Meinel
Add the ability to report if the lock changes from underneath you
328
           Lock1 to release and acquire the lockdir. This resets the 'check'
329
           lock.
330
        4) Lock2 acquires the 'check' lock, and checks again. It notices
331
           that the holder of the lock has changed, and so reports a new 
332
           lock holder.
1957.1.11 by John Arbash Meinel
Switch from locking the peek() to locking attempt_lock(), which is much more stable
333
        5) Thread1 blocks on the 'checked' lock, this time, it completely
1957.1.7 by John Arbash Meinel
Add the ability to report if the lock changes from underneath you
334
           unlocks the lockdir, allowing Lock2 to acquire the lock.
335
        """
336
337
        wait_to_check_lock = Lock()
1957.1.11 by John Arbash Meinel
Switch from locking the peek() to locking attempt_lock(), which is much more stable
338
        wait_until_checked_lock = Lock()
1957.1.7 by John Arbash Meinel
Add the ability to report if the lock changes from underneath you
339
340
        wait_to_check_lock.acquire()
1957.1.11 by John Arbash Meinel
Switch from locking the peek() to locking attempt_lock(), which is much more stable
341
        wait_until_checked_lock.acquire()
342
        note('locked check and checked locks')
1957.1.7 by John Arbash Meinel
Add the ability to report if the lock changes from underneath you
343
344
        class LockDir1(LockDir):
345
            """Use the synchronization points for the first lock."""
346
347
            def attempt_lock(self):
348
                # Once we have acquired the lock, it is okay for
349
                # the other lock to check it
350
                try:
351
                    return super(LockDir1, self).attempt_lock()
352
                finally:
353
                    note('lock1: releasing check lock')
354
                    wait_to_check_lock.release()
355
356
        class LockDir2(LockDir):
357
            """Use the synchronization points for the second lock."""
358
1957.1.11 by John Arbash Meinel
Switch from locking the peek() to locking attempt_lock(), which is much more stable
359
            def attempt_lock(self):
1957.1.7 by John Arbash Meinel
Add the ability to report if the lock changes from underneath you
360
                note('lock2: waiting for check lock')
361
                wait_to_check_lock.acquire()
362
                note('lock2: acquired check lock')
363
                try:
1957.1.11 by John Arbash Meinel
Switch from locking the peek() to locking attempt_lock(), which is much more stable
364
                    return super(LockDir2, self).attempt_lock()
1957.1.7 by John Arbash Meinel
Add the ability to report if the lock changes from underneath you
365
                finally:
1957.1.11 by John Arbash Meinel
Switch from locking the peek() to locking attempt_lock(), which is much more stable
366
                    note('lock2: releasing checked lock')
367
                    wait_until_checked_lock.release()
1957.1.7 by John Arbash Meinel
Add the ability to report if the lock changes from underneath you
368
369
        t = self.get_transport()
370
        lf1 = LockDir1(t, 'test_lock')
371
        lf1.create()
372
373
        lf2 = LockDir2(t, 'test_lock')
374
        self.setup_log_reporter(lf2)
375
376
        def wait_and_switch():
377
            lf1.attempt_lock()
1957.1.11 by John Arbash Meinel
Switch from locking the peek() to locking attempt_lock(), which is much more stable
378
            # Block until lock2 has had a chance to check
379
            note('lock1: waiting 1 for checked lock')
380
            wait_until_checked_lock.acquire()
381
            note('lock1: acquired for checked lock')
1957.1.7 by John Arbash Meinel
Add the ability to report if the lock changes from underneath you
382
            note('lock1: released lockdir')
383
            lf1.unlock()
384
            note('lock1: acquiring lockdir')
385
            # Create a new nonce, so the lock looks different.
386
            lf1.nonce = osutils.rand_chars(20)
387
            lf1.lock_write()
388
            note('lock1: acquired lockdir')
389
390
            # Block until lock2 has peeked again
1957.1.11 by John Arbash Meinel
Switch from locking the peek() to locking attempt_lock(), which is much more stable
391
            note('lock1: waiting 2 for checked lock')
392
            wait_until_checked_lock.acquire()
393
            note('lock1: acquired for checked lock')
394
            # Now unlock, and let lock 2 grab the lock
1957.1.7 by John Arbash Meinel
Add the ability to report if the lock changes from underneath you
395
            lf1.unlock()
396
            wait_to_check_lock.release()
397
398
        unlocker = Thread(target=wait_and_switch)
399
        unlocker.start()
400
        try:
1957.1.11 by John Arbash Meinel
Switch from locking the peek() to locking attempt_lock(), which is much more stable
401
            # Wait and play against the other thread
2709.1.1 by Martin Pool
Make TestLockDir.test_35_wait_lock_changing less timing-sensitive
402
            lf2.wait_lock(timeout=20.0, poll=0.01)
1957.1.7 by John Arbash Meinel
Add the ability to report if the lock changes from underneath you
403
        finally:
404
            unlocker.join()
405
        lf2.unlock()
406
407
        # There should be 2 reports, because the lock changed
408
        lock_base = lf2.transport.abspath(lf2.path)
409
        self.assertEqual(2, len(self._logged_reports))
1957.1.13 by John Arbash Meinel
Change to reporting the time when we will stop trying to grab the lock
410
1957.1.15 by John Arbash Meinel
Review feedback from Robert
411
        self.assertEqual('%s %s\n'
412
                         '%s\n%s\n'
413
                         'Will continue to try until %s\n',
414
                         self._logged_reports[0][0])
415
        args = self._logged_reports[0][1]
416
        self.assertEqual('Unable to obtain', args[0])
417
        self.assertEqual('lock %s' % (lock_base,), args[1])
418
        self.assertStartsWith(args[2], 'held by ')
419
        self.assertStartsWith(args[3], 'locked ')
420
        self.assertEndsWith(args[3], ' ago')
421
        self.assertContainsRe(args[4], r'\d\d:\d\d:\d\d')
1957.1.13 by John Arbash Meinel
Change to reporting the time when we will stop trying to grab the lock
422
1957.1.15 by John Arbash Meinel
Review feedback from Robert
423
        self.assertEqual('%s %s\n'
424
                         '%s\n%s\n'
425
                         'Will continue to try until %s\n',
426
                         self._logged_reports[1][0])
427
        args = self._logged_reports[1][1]
428
        self.assertEqual('Lock owner changed for', args[0])
429
        self.assertEqual('lock %s' % (lock_base,), args[1])
430
        self.assertStartsWith(args[2], 'held by ')
431
        self.assertStartsWith(args[3], 'locked ')
432
        self.assertEndsWith(args[3], ' ago')
433
        self.assertContainsRe(args[4], r'\d\d:\d\d:\d\d')
1957.1.2 by John Arbash Meinel
Switch the default from instantly aborting, to waiting as long as 1 minute (down from 5 minutes)
434
1553.5.20 by Martin Pool
Start adding LockDir.confirm() method
435
    def test_40_confirm_easy(self):
436
        """Confirm a lock that's already held"""
437
        t = self.get_transport()
438
        lf1 = LockDir(t, 'test_lock')
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
439
        lf1.create()
1553.5.20 by Martin Pool
Start adding LockDir.confirm() method
440
        lf1.attempt_lock()
441
        lf1.confirm()
442
443
    def test_41_confirm_not_held(self):
444
        """Confirm a lock that's already held"""
445
        t = self.get_transport()
446
        lf1 = LockDir(t, 'test_lock')
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
447
        lf1.create()
1553.5.20 by Martin Pool
Start adding LockDir.confirm() method
448
        self.assertRaises(LockNotHeld, lf1.confirm)
1553.5.23 by Martin Pool
Start LockDir.confirm method and LockBroken exception
449
450
    def test_42_confirm_broken_manually(self):
451
        """Confirm a lock broken by hand"""
452
        t = self.get_transport()
453
        lf1 = LockDir(t, 'test_lock')
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
454
        lf1.create()
1553.5.23 by Martin Pool
Start LockDir.confirm method and LockBroken exception
455
        lf1.attempt_lock()
456
        t.move('test_lock', 'lock_gone_now')
457
        self.assertRaises(LockBroken, lf1.confirm)
1553.5.25 by Martin Pool
New LockDir.force_break and simple test case
458
459
    def test_43_break(self):
460
        """Break a lock whose caller has forgotten it"""
461
        t = self.get_transport()
462
        lf1 = LockDir(t, 'test_lock')
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
463
        lf1.create()
1553.5.25 by Martin Pool
New LockDir.force_break and simple test case
464
        lf1.attempt_lock()
465
        # we incorrectly discard the lock object without unlocking it
466
        del lf1
467
        # someone else sees it's still locked
468
        lf2 = LockDir(t, 'test_lock')
1553.5.27 by Martin Pool
Confirm that only the intended holder of a lock was broken.
469
        holder_info = lf2.peek()
470
        self.assertTrue(holder_info)
471
        lf2.force_break(holder_info)
1553.5.25 by Martin Pool
New LockDir.force_break and simple test case
472
        # now we should be able to take it
473
        lf2.attempt_lock()
474
        lf2.confirm()
1553.5.26 by Martin Pool
Breaking an already-released lock should just succeed
475
476
    def test_44_break_already_released(self):
477
        """Lock break races with regular release"""
478
        t = self.get_transport()
479
        lf1 = LockDir(t, 'test_lock')
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
480
        lf1.create()
1553.5.26 by Martin Pool
Breaking an already-released lock should just succeed
481
        lf1.attempt_lock()
482
        # someone else sees it's still locked
483
        lf2 = LockDir(t, 'test_lock')
484
        holder_info = lf2.peek()
485
        # in the interim the lock is released
486
        lf1.unlock()
487
        # break should succeed
1553.5.27 by Martin Pool
Confirm that only the intended holder of a lock was broken.
488
        lf2.force_break(holder_info)
1553.5.26 by Martin Pool
Breaking an already-released lock should just succeed
489
        # now we should be able to take it
490
        lf2.attempt_lock()
491
        lf2.confirm()
492
1553.5.27 by Martin Pool
Confirm that only the intended holder of a lock was broken.
493
    def test_45_break_mismatch(self):
494
        """Lock break races with someone else acquiring it"""
495
        t = self.get_transport()
496
        lf1 = LockDir(t, 'test_lock')
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
497
        lf1.create()
1553.5.27 by Martin Pool
Confirm that only the intended holder of a lock was broken.
498
        lf1.attempt_lock()
499
        # someone else sees it's still locked
500
        lf2 = LockDir(t, 'test_lock')
501
        holder_info = lf2.peek()
502
        # in the interim the lock is released
503
        lf1.unlock()
504
        lf3 = LockDir(t, 'test_lock')
505
        lf3.attempt_lock()
506
        # break should now *fail*
507
        self.assertRaises(LockBreakMismatch, lf2.force_break,
508
                          holder_info)
509
        lf3.unlock()
1553.5.54 by Martin Pool
Add LockDir.read_lock fake method
510
511
    def test_46_fake_read_lock(self):
512
        t = self.get_transport()
513
        lf1 = LockDir(t, 'test_lock')
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
514
        lf1.create()
1553.5.54 by Martin Pool
Add LockDir.read_lock fake method
515
        lf1.lock_read()
516
        lf1.unlock()
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
517
518
    def test_50_lockdir_representation(self):
519
        """Check the on-disk representation of LockDirs is as expected.
520
521
        There should always be a top-level directory named by the lock.
522
        When the lock is held, there should be a lockname/held directory 
523
        containing an info file.
524
        """
525
        t = self.get_transport()
526
        lf1 = LockDir(t, 'test_lock')
527
        lf1.create()
528
        self.assertTrue(t.has('test_lock'))
529
        lf1.lock_write()
530
        self.assertTrue(t.has('test_lock/held/info'))
531
        lf1.unlock()
532
        self.assertFalse(t.has('test_lock/held/info'))
1687.1.5 by Robert Collins
Add break_lock utility function to LockDir.
533
534
    def test_break_lock(self):
535
        # the ui based break_lock routine should Just Work (tm)
536
        ld1 = self.get_lock()
537
        ld2 = self.get_lock()
538
        ld1.create()
539
        ld1.lock_write()
1687.1.6 by Robert Collins
Extend LockableFiles to support break_lock() calls.
540
        # do this without IO redirection to ensure it doesn't prompt.
541
        self.assertRaises(AssertionError, ld1.break_lock)
1687.1.5 by Robert Collins
Add break_lock utility function to LockDir.
542
        orig_factory = bzrlib.ui.ui_factory
543
        # silent ui - no need for stdout
544
        bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
545
        bzrlib.ui.ui_factory.stdin = StringIO("y\n")
546
        try:
547
            ld2.break_lock()
548
            self.assertRaises(LockBroken, ld1.unlock)
549
        finally:
550
            bzrlib.ui.ui_factory = orig_factory
1955.1.1 by John Arbash Meinel
LockDir can create the root directory if it fails to create a pending directory due to NoSuchFile.
551
552
    def test_create_missing_base_directory(self):
553
        """If LockDir.path doesn't exist, it can be created
554
555
        Some people manually remove the entire lock/ directory trying
556
        to unlock a stuck repository/branch/etc. Rather than failing
557
        after that, just create the lock directory when needed.
558
        """
559
        t = self.get_transport()
560
        lf1 = LockDir(t, 'test_lock')
561
562
        lf1.create()
563
        self.failUnless(t.has('test_lock'))
564
565
        t.rmdir('test_lock')
566
        self.failIf(t.has('test_lock'))
567
568
        # This will create 'test_lock' if it needs to
569
        lf1.lock_write()
570
        self.failUnless(t.has('test_lock'))
571
        self.failUnless(t.has('test_lock/held/info'))
572
573
        lf1.unlock()
574
        self.failIf(t.has('test_lock/held/info'))
1957.1.6 by John Arbash Meinel
[merge] bzr.dev 2009
575
1957.1.5 by John Arbash Meinel
Create a helper function for formatting lock information
576
    def test__format_lock_info(self):
577
        ld1 = self.get_lock()
578
        ld1.create()
579
        ld1.lock_write()
580
        try:
581
            info_list = ld1._format_lock_info(ld1.peek())
582
        finally:
583
            ld1.unlock()
584
        self.assertEqual('lock %s' % (ld1.transport.abspath(ld1.path),),
585
                         info_list[0])
586
        self.assertContainsRe(info_list[1],
587
                              r'^held by .* on host .* \[process #\d*\]$')
588
        self.assertContainsRe(info_list[2], r'locked \d+ seconds? ago$')
2055.2.1 by John Arbash Meinel
Make LockDir less sensitive to invalid configuration of email
589
590
    def test_lock_without_email(self):
591
        global_config = config.GlobalConfig()
592
        # Intentionally has no email address
593
        global_config.set_user_option('email', 'User Identity')
594
        ld1 = self.get_lock()
595
        ld1.create()
596
        ld1.lock_write()
597
        ld1.unlock()
1551.10.3 by Aaron Bentley
Lock attempts don't treat permission problems as lock contention
598
599
    def test_lock_permission(self):
1551.10.4 by Aaron Bentley
Update to skip on win32
600
        if not osutils.supports_posix_readonly():
601
            raise tests.TestSkipped('Cannot induce a permission failure')
1551.10.3 by Aaron Bentley
Lock attempts don't treat permission problems as lock contention
602
        ld1 = self.get_lock()
603
        lock_path = ld1.transport.local_abspath('test_lock')
604
        os.mkdir(lock_path)
605
        osutils.make_readonly(lock_path)
2872.5.1 by Martin Pool
Avoid internal error tracebacks on failure to lock on readonly transport (#129701).
606
        self.assertRaises(errors.LockFailed, ld1.attempt_lock)
2555.3.5 by Martin Pool
Return token directly from LockDir.acquire to avoid unnecessary peek()
607
608
    def test_lock_by_token(self):
609
        ld1 = self.get_lock()
610
        token = ld1.lock_write()
611
        self.assertNotEqual(None, token)
612
        ld2 = self.get_lock()
613
        t2 = ld2.lock_write(token)
614
        self.assertEqual(token, t2)
2555.3.9 by Martin Pool
Add test and fix for locking robustly when rename of directories doesn't act as a mutex (thank John)
615
616
    def test_lock_with_buggy_rename(self):
617
        # test that lock acquisition handles servers which pretend they
618
        # renamed correctly but that actually fail
619
        t = transport.get_transport('brokenrename+' + self.get_url())
620
        ld1 = LockDir(t, 'test_lock')
621
        ld1.create()
622
        ld1.attempt_lock()
623
        ld2 = LockDir(t, 'test_lock')
2555.3.14 by Martin Pool
Better handling in LockDir of rename that moves one directory within another
624
        # we should fail to lock
625
        e = self.assertRaises(errors.LockContention, ld2.attempt_lock)
626
        # now the original caller should succeed in unlocking
627
        ld1.unlock()
628
        # and there should be nothing left over
629
        self.assertEquals([], t.list_dir('test_lock'))
2555.3.12 by Martin Pool
Add test for https://bugs.launchpad.net/bzr/+bug/109169 -- test_failed_lock_leaves_no_trash
630
631
    def test_failed_lock_leaves_no_trash(self):
632
        # if we fail to acquire the lock, we don't leave pending directories
633
        # behind -- https://bugs.launchpad.net/bzr/+bug/109169
634
        ld1 = self.get_lock()
635
        ld2 = self.get_lock()
636
        # should be nothing before we start
637
        ld1.create()
638
        t = self.get_transport().clone('test_lock')
639
        def check_dir(a):
640
            self.assertEquals(a, t.list_dir('.'))
641
        check_dir([])
642
        # when held, that's all we see
643
        ld1.attempt_lock()
644
        check_dir(['held'])
645
        # second guy should fail
646
        self.assertRaises(errors.LockContention, ld2.attempt_lock)
647
        # no kibble
648
        check_dir(['held'])