~bzr-pqm/bzr/bzr.dev

3331.3.5 by Martin Pool
Move physical lock hooks onto new PhysicalLock class variable
1
# Copyright (C) 2006, 2007, 2008 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,
3331.3.13 by Martin Pool
Fix up imports
28
    lock,
1957.1.7 by John Arbash Meinel
Add the ability to report if the lock changes from underneath you
29
    osutils,
1551.10.4 by Aaron Bentley
Update to skip on win32
30
    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)
31
    transport,
1957.1.7 by John Arbash Meinel
Add the ability to report if the lock changes from underneath you
32
    )
1553.5.27 by Martin Pool
Confirm that only the intended holder of a lock was broken.
33
from bzrlib.errors import (
2872.5.1 by Martin Pool
Avoid internal error tracebacks on failure to lock on readonly transport (#129701).
34
    LockBreakMismatch,
35
    LockBroken,
36
    LockContention,
37
    LockError,
38
    LockFailed,
39
    LockNotHeld,
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))
3441.4.2 by Martin Albisetti
Fixed failing tests
194
        lock_url = lf2.transport.abspath(lf2.path)
1957.1.9 by John Arbash Meinel
Change default timeouts, and report differently the first failure
195
        self.assertEqual('%s %s\n'
1957.1.7 by John Arbash Meinel
Add the ability to report if the lock changes from underneath you
196
                         '%s\n%s\n'
3441.4.2 by Martin Albisetti
Fixed failing tests
197
                         'Will continue to try until %s, unless '
198
                         'you press Ctrl-C\n'
199
                         'If you\'re sure that it\'s not being '
200
                         'modified, use bzr break-lock %s',
1957.1.7 by John Arbash Meinel
Add the ability to report if the lock changes from underneath you
201
                         self._logged_reports[0][0])
202
        args = self._logged_reports[0][1]
1957.1.9 by John Arbash Meinel
Change default timeouts, and report differently the first failure
203
        self.assertEqual('Unable to obtain', args[0])
204
        self.assertEqual('lock %s' % (lock_base,), args[1])
205
        self.assertStartsWith(args[2], 'held by ')
206
        self.assertStartsWith(args[3], 'locked ')
207
        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
208
        self.assertContainsRe(args[4], r'\d\d:\d\d:\d\d')
1553.5.12 by Martin Pool
New LockDir locking mechanism
209
210
    def test_31_lock_wait_easy(self):
211
        """Succeed when waiting on a lock with no contention.
212
        """
213
        t = self.get_transport()
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
214
        lf1 = LockDir(t, 'test_lock')
215
        lf1.create()
1957.1.1 by John Arbash Meinel
Report to the user when we are spinning on a lock
216
        self.setup_log_reporter(lf1)
1553.5.12 by Martin Pool
New LockDir locking mechanism
217
        try:
218
            before = time.time()
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
219
            lf1.wait_lock(timeout=0.4, poll=0.1)
1553.5.12 by Martin Pool
New LockDir locking mechanism
220
            after = time.time()
221
            self.assertTrue(after - before <= 1.0)
222
        finally:
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
223
            lf1.unlock()
1957.1.1 by John Arbash Meinel
Report to the user when we are spinning on a lock
224
        self.assertEqual([], self._logged_reports)
1553.5.12 by Martin Pool
New LockDir locking mechanism
225
1551.15.18 by Aaron Bentley
Skip itermittently-failing test instead of deleting it
226
    def test_32_lock_wait_succeed(self):
227
        """Succeed when trying to acquire a lock that gets released
228
229
        One thread holds on a lock and then releases it; another 
230
        tries to lock it.
231
        """
1551.15.22 by Aaron Bentley
Redo test skip
232
        # This test sometimes fails like this:
233
        # Traceback (most recent call last):
234
235
        #   File "/home/pqm/bzr-pqm-workdir/home/+trunk/bzrlib/tests/
236
        # test_lockdir.py", line 247, in test_32_lock_wait_succeed
237
        #     self.assertEqual(1, len(self._logged_reports))
238
        # AssertionError: not equal:
239
        # a = 1
240
        # b = 0
241
        raise tests.TestSkipped("Test fails intermittently")
1551.15.18 by Aaron Bentley
Skip itermittently-failing test instead of deleting it
242
        t = self.get_transport()
243
        lf1 = LockDir(t, 'test_lock')
244
        lf1.create()
245
        lf1.attempt_lock()
246
247
        def wait_and_unlock():
248
            time.sleep(0.1)
249
            lf1.unlock()
250
        unlocker = Thread(target=wait_and_unlock)
251
        unlocker.start()
252
        try:
253
            lf2 = LockDir(t, 'test_lock')
254
            self.setup_log_reporter(lf2)
255
            before = time.time()
256
            # wait and then lock
257
            lf2.wait_lock(timeout=0.4, poll=0.1)
258
            after = time.time()
259
            self.assertTrue(after - before <= 1.0)
260
        finally:
261
            unlocker.join()
262
263
        # There should be only 1 report, even though it should have to
264
        # wait for a while
265
        lock_base = lf2.transport.abspath(lf2.path)
266
        self.assertEqual(1, len(self._logged_reports))
267
        self.assertEqual('%s %s\n'
268
                         '%s\n%s\n'
269
                         'Will continue to try until %s\n',
270
                         self._logged_reports[0][0])
271
        args = self._logged_reports[0][1]
272
        self.assertEqual('Unable to obtain', args[0])
273
        self.assertEqual('lock %s' % (lock_base,), args[1])
274
        self.assertStartsWith(args[2], 'held by ')
275
        self.assertStartsWith(args[3], 'locked ')
276
        self.assertEndsWith(args[3], ' ago')
277
        self.assertContainsRe(args[4], r'\d\d:\d\d:\d\d')
278
1957.1.2 by John Arbash Meinel
Switch the default from instantly aborting, to waiting as long as 1 minute (down from 5 minutes)
279
    def test_34_lock_write_waits(self):
280
        """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.
281
        # the test suite sets the default to 0 to make deadlocks fail fast.
282
        # change it for this test, as we want to try a manual deadlock.
2631.1.4 by Aaron Bentley
Fix test skip
283
        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.
284
        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)
285
        t = self.get_transport()
286
        lf1 = LockDir(t, 'test_lock')
287
        lf1.create()
288
        lf1.attempt_lock()
289
290
        def wait_and_unlock():
291
            time.sleep(0.1)
292
            lf1.unlock()
293
        unlocker = Thread(target=wait_and_unlock)
294
        unlocker.start()
295
        try:
296
            lf2 = LockDir(t, 'test_lock')
297
            self.setup_log_reporter(lf2)
298
            before = time.time()
299
            # wait and then lock
300
            lf2.lock_write()
301
            after = time.time()
302
        finally:
303
            unlocker.join()
304
305
        # There should be only 1 report, even though it should have to
306
        # wait for a while
307
        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
308
        self.assertEqual(1, len(self._logged_reports))
1957.1.9 by John Arbash Meinel
Change default timeouts, and report differently the first failure
309
        self.assertEqual('%s %s\n'
1957.1.7 by John Arbash Meinel
Add the ability to report if the lock changes from underneath you
310
                         '%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
311
                         '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
312
                         self._logged_reports[0][0])
313
        args = self._logged_reports[0][1]
1957.1.9 by John Arbash Meinel
Change default timeouts, and report differently the first failure
314
        self.assertEqual('Unable to obtain', args[0])
315
        self.assertEqual('lock %s' % (lock_base,), args[1])
316
        self.assertStartsWith(args[2], 'held by ')
317
        self.assertStartsWith(args[3], 'locked ')
318
        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
319
        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
320
321
    def test_35_wait_lock_changing(self):
322
        """LockDir.wait_lock() will report if the lock changes underneath.
323
        
324
        This is the stages we want to happen:
325
326
        0) Synchronization locks are created and locked.
327
        1) Lock1 obtains the lockdir, and releases the 'check' lock.
328
        2) Lock2 grabs the 'check' lock, and checks the lockdir.
329
           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
330
           and unsets the 'checked' lock.
331
        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
332
           Lock1 to release and acquire the lockdir. This resets the 'check'
333
           lock.
334
        4) Lock2 acquires the 'check' lock, and checks again. It notices
335
           that the holder of the lock has changed, and so reports a new 
336
           lock holder.
1957.1.11 by John Arbash Meinel
Switch from locking the peek() to locking attempt_lock(), which is much more stable
337
        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
338
           unlocks the lockdir, allowing Lock2 to acquire the lock.
339
        """
340
3497.1.1 by Martin Pool
xfail test_35_wait_lock_changing
341
        raise tests.KnownFailure(
342
            "timing dependency in lock tests (#213182)")
343
1957.1.7 by John Arbash Meinel
Add the ability to report if the lock changes from underneath you
344
        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
345
        wait_until_checked_lock = Lock()
1957.1.7 by John Arbash Meinel
Add the ability to report if the lock changes from underneath you
346
347
        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
348
        wait_until_checked_lock.acquire()
349
        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
350
351
        class LockDir1(LockDir):
352
            """Use the synchronization points for the first lock."""
353
354
            def attempt_lock(self):
355
                # Once we have acquired the lock, it is okay for
356
                # the other lock to check it
357
                try:
358
                    return super(LockDir1, self).attempt_lock()
359
                finally:
360
                    note('lock1: releasing check lock')
361
                    wait_to_check_lock.release()
362
363
        class LockDir2(LockDir):
364
            """Use the synchronization points for the second lock."""
365
1957.1.11 by John Arbash Meinel
Switch from locking the peek() to locking attempt_lock(), which is much more stable
366
            def attempt_lock(self):
1957.1.7 by John Arbash Meinel
Add the ability to report if the lock changes from underneath you
367
                note('lock2: waiting for check lock')
368
                wait_to_check_lock.acquire()
369
                note('lock2: acquired check lock')
370
                try:
1957.1.11 by John Arbash Meinel
Switch from locking the peek() to locking attempt_lock(), which is much more stable
371
                    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
372
                finally:
1957.1.11 by John Arbash Meinel
Switch from locking the peek() to locking attempt_lock(), which is much more stable
373
                    note('lock2: releasing checked lock')
374
                    wait_until_checked_lock.release()
1957.1.7 by John Arbash Meinel
Add the ability to report if the lock changes from underneath you
375
376
        t = self.get_transport()
377
        lf1 = LockDir1(t, 'test_lock')
378
        lf1.create()
379
380
        lf2 = LockDir2(t, 'test_lock')
381
        self.setup_log_reporter(lf2)
382
383
        def wait_and_switch():
384
            lf1.attempt_lock()
1957.1.11 by John Arbash Meinel
Switch from locking the peek() to locking attempt_lock(), which is much more stable
385
            # Block until lock2 has had a chance to check
386
            note('lock1: waiting 1 for checked lock')
387
            wait_until_checked_lock.acquire()
388
            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
389
            note('lock1: released lockdir')
390
            lf1.unlock()
391
            note('lock1: acquiring lockdir')
392
            # Create a new nonce, so the lock looks different.
393
            lf1.nonce = osutils.rand_chars(20)
394
            lf1.lock_write()
395
            note('lock1: acquired lockdir')
396
397
            # 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
398
            note('lock1: waiting 2 for checked lock')
399
            wait_until_checked_lock.acquire()
400
            note('lock1: acquired for checked lock')
401
            # 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
402
            lf1.unlock()
403
            wait_to_check_lock.release()
404
405
        unlocker = Thread(target=wait_and_switch)
406
        unlocker.start()
407
        try:
1957.1.11 by John Arbash Meinel
Switch from locking the peek() to locking attempt_lock(), which is much more stable
408
            # Wait and play against the other thread
2709.1.1 by Martin Pool
Make TestLockDir.test_35_wait_lock_changing less timing-sensitive
409
            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
410
        finally:
411
            unlocker.join()
412
        lf2.unlock()
413
414
        # There should be 2 reports, because the lock changed
415
        lock_base = lf2.transport.abspath(lf2.path)
416
        self.assertEqual(2, len(self._logged_reports))
3441.4.2 by Martin Albisetti
Fixed failing tests
417
        lock_url = lf2.transport.abspath(lf2.path)
1957.1.15 by John Arbash Meinel
Review feedback from Robert
418
        self.assertEqual('%s %s\n'
419
                         '%s\n%s\n'
3441.4.2 by Martin Albisetti
Fixed failing tests
420
                         'Will continue to try until %s, unless '
421
                         'you press Ctrl-C\n'
422
                         'If you\'re sure that it\'s not being '
423
                         'modified, use bzr break-lock %s',
1957.1.15 by John Arbash Meinel
Review feedback from Robert
424
                         self._logged_reports[0][0])
425
        args = self._logged_reports[0][1]
426
        self.assertEqual('Unable to obtain', args[0])
427
        self.assertEqual('lock %s' % (lock_base,), args[1])
428
        self.assertStartsWith(args[2], 'held by ')
429
        self.assertStartsWith(args[3], 'locked ')
430
        self.assertEndsWith(args[3], ' ago')
431
        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
432
1957.1.15 by John Arbash Meinel
Review feedback from Robert
433
        self.assertEqual('%s %s\n'
434
                         '%s\n%s\n'
3441.4.2 by Martin Albisetti
Fixed failing tests
435
                         'Will continue to try until %s, unless '
436
                         'you press Ctrl-C\n'
437
                         'If you\'re sure that it\'s not being '
438
                         'modified, use bzr break-lock %s',
1957.1.15 by John Arbash Meinel
Review feedback from Robert
439
                         self._logged_reports[1][0])
440
        args = self._logged_reports[1][1]
441
        self.assertEqual('Lock owner changed for', args[0])
442
        self.assertEqual('lock %s' % (lock_base,), args[1])
443
        self.assertStartsWith(args[2], 'held by ')
444
        self.assertStartsWith(args[3], 'locked ')
445
        self.assertEndsWith(args[3], ' ago')
446
        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)
447
1553.5.20 by Martin Pool
Start adding LockDir.confirm() method
448
    def test_40_confirm_easy(self):
449
        """Confirm a lock that's already held"""
450
        t = self.get_transport()
451
        lf1 = LockDir(t, 'test_lock')
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
452
        lf1.create()
1553.5.20 by Martin Pool
Start adding LockDir.confirm() method
453
        lf1.attempt_lock()
454
        lf1.confirm()
455
456
    def test_41_confirm_not_held(self):
457
        """Confirm a lock that's already held"""
458
        t = self.get_transport()
459
        lf1 = LockDir(t, 'test_lock')
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
460
        lf1.create()
1553.5.20 by Martin Pool
Start adding LockDir.confirm() method
461
        self.assertRaises(LockNotHeld, lf1.confirm)
1553.5.23 by Martin Pool
Start LockDir.confirm method and LockBroken exception
462
463
    def test_42_confirm_broken_manually(self):
464
        """Confirm a lock broken by hand"""
465
        t = self.get_transport()
466
        lf1 = LockDir(t, 'test_lock')
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
467
        lf1.create()
1553.5.23 by Martin Pool
Start LockDir.confirm method and LockBroken exception
468
        lf1.attempt_lock()
469
        t.move('test_lock', 'lock_gone_now')
470
        self.assertRaises(LockBroken, lf1.confirm)
1553.5.25 by Martin Pool
New LockDir.force_break and simple test case
471
472
    def test_43_break(self):
473
        """Break a lock whose caller has forgotten it"""
474
        t = self.get_transport()
475
        lf1 = LockDir(t, 'test_lock')
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
476
        lf1.create()
1553.5.25 by Martin Pool
New LockDir.force_break and simple test case
477
        lf1.attempt_lock()
478
        # we incorrectly discard the lock object without unlocking it
479
        del lf1
480
        # someone else sees it's still locked
481
        lf2 = LockDir(t, 'test_lock')
1553.5.27 by Martin Pool
Confirm that only the intended holder of a lock was broken.
482
        holder_info = lf2.peek()
483
        self.assertTrue(holder_info)
484
        lf2.force_break(holder_info)
1553.5.25 by Martin Pool
New LockDir.force_break and simple test case
485
        # now we should be able to take it
486
        lf2.attempt_lock()
487
        lf2.confirm()
1553.5.26 by Martin Pool
Breaking an already-released lock should just succeed
488
489
    def test_44_break_already_released(self):
490
        """Lock break races with regular release"""
491
        t = self.get_transport()
492
        lf1 = LockDir(t, 'test_lock')
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
493
        lf1.create()
1553.5.26 by Martin Pool
Breaking an already-released lock should just succeed
494
        lf1.attempt_lock()
495
        # someone else sees it's still locked
496
        lf2 = LockDir(t, 'test_lock')
497
        holder_info = lf2.peek()
498
        # in the interim the lock is released
499
        lf1.unlock()
500
        # break should succeed
1553.5.27 by Martin Pool
Confirm that only the intended holder of a lock was broken.
501
        lf2.force_break(holder_info)
1553.5.26 by Martin Pool
Breaking an already-released lock should just succeed
502
        # now we should be able to take it
503
        lf2.attempt_lock()
504
        lf2.confirm()
505
1553.5.27 by Martin Pool
Confirm that only the intended holder of a lock was broken.
506
    def test_45_break_mismatch(self):
507
        """Lock break races with someone else acquiring it"""
508
        t = self.get_transport()
509
        lf1 = LockDir(t, 'test_lock')
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
510
        lf1.create()
1553.5.27 by Martin Pool
Confirm that only the intended holder of a lock was broken.
511
        lf1.attempt_lock()
512
        # someone else sees it's still locked
513
        lf2 = LockDir(t, 'test_lock')
514
        holder_info = lf2.peek()
515
        # in the interim the lock is released
516
        lf1.unlock()
517
        lf3 = LockDir(t, 'test_lock')
518
        lf3.attempt_lock()
519
        # break should now *fail*
520
        self.assertRaises(LockBreakMismatch, lf2.force_break,
521
                          holder_info)
522
        lf3.unlock()
1553.5.54 by Martin Pool
Add LockDir.read_lock fake method
523
524
    def test_46_fake_read_lock(self):
525
        t = self.get_transport()
526
        lf1 = LockDir(t, 'test_lock')
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
527
        lf1.create()
1553.5.54 by Martin Pool
Add LockDir.read_lock fake method
528
        lf1.lock_read()
529
        lf1.unlock()
1553.5.58 by Martin Pool
Change LockDirs to format "lock-name/held/info"
530
531
    def test_50_lockdir_representation(self):
532
        """Check the on-disk representation of LockDirs is as expected.
533
534
        There should always be a top-level directory named by the lock.
535
        When the lock is held, there should be a lockname/held directory 
536
        containing an info file.
537
        """
538
        t = self.get_transport()
539
        lf1 = LockDir(t, 'test_lock')
540
        lf1.create()
541
        self.assertTrue(t.has('test_lock'))
542
        lf1.lock_write()
543
        self.assertTrue(t.has('test_lock/held/info'))
544
        lf1.unlock()
545
        self.assertFalse(t.has('test_lock/held/info'))
1687.1.5 by Robert Collins
Add break_lock utility function to LockDir.
546
547
    def test_break_lock(self):
548
        # the ui based break_lock routine should Just Work (tm)
549
        ld1 = self.get_lock()
550
        ld2 = self.get_lock()
551
        ld1.create()
552
        ld1.lock_write()
1687.1.6 by Robert Collins
Extend LockableFiles to support break_lock() calls.
553
        # do this without IO redirection to ensure it doesn't prompt.
554
        self.assertRaises(AssertionError, ld1.break_lock)
1687.1.5 by Robert Collins
Add break_lock utility function to LockDir.
555
        orig_factory = bzrlib.ui.ui_factory
556
        # silent ui - no need for stdout
557
        bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
558
        bzrlib.ui.ui_factory.stdin = StringIO("y\n")
559
        try:
560
            ld2.break_lock()
561
            self.assertRaises(LockBroken, ld1.unlock)
562
        finally:
563
            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.
564
565
    def test_create_missing_base_directory(self):
566
        """If LockDir.path doesn't exist, it can be created
567
568
        Some people manually remove the entire lock/ directory trying
569
        to unlock a stuck repository/branch/etc. Rather than failing
570
        after that, just create the lock directory when needed.
571
        """
572
        t = self.get_transport()
573
        lf1 = LockDir(t, 'test_lock')
574
575
        lf1.create()
576
        self.failUnless(t.has('test_lock'))
577
578
        t.rmdir('test_lock')
579
        self.failIf(t.has('test_lock'))
580
581
        # This will create 'test_lock' if it needs to
582
        lf1.lock_write()
583
        self.failUnless(t.has('test_lock'))
584
        self.failUnless(t.has('test_lock/held/info'))
585
586
        lf1.unlock()
587
        self.failIf(t.has('test_lock/held/info'))
1957.1.6 by John Arbash Meinel
[merge] bzr.dev 2009
588
1957.1.5 by John Arbash Meinel
Create a helper function for formatting lock information
589
    def test__format_lock_info(self):
590
        ld1 = self.get_lock()
591
        ld1.create()
592
        ld1.lock_write()
593
        try:
594
            info_list = ld1._format_lock_info(ld1.peek())
595
        finally:
596
            ld1.unlock()
597
        self.assertEqual('lock %s' % (ld1.transport.abspath(ld1.path),),
598
                         info_list[0])
599
        self.assertContainsRe(info_list[1],
600
                              r'^held by .* on host .* \[process #\d*\]$')
601
        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
602
603
    def test_lock_without_email(self):
604
        global_config = config.GlobalConfig()
605
        # Intentionally has no email address
606
        global_config.set_user_option('email', 'User Identity')
607
        ld1 = self.get_lock()
608
        ld1.create()
609
        ld1.lock_write()
610
        ld1.unlock()
1551.10.3 by Aaron Bentley
Lock attempts don't treat permission problems as lock contention
611
612
    def test_lock_permission(self):
1551.10.4 by Aaron Bentley
Update to skip on win32
613
        if not osutils.supports_posix_readonly():
3107.2.2 by John Arbash Meinel
feedback from Martin.
614
            raise tests.TestSkipped('Cannot induce a permission failure')
1551.10.3 by Aaron Bentley
Lock attempts don't treat permission problems as lock contention
615
        ld1 = self.get_lock()
616
        lock_path = ld1.transport.local_abspath('test_lock')
617
        os.mkdir(lock_path)
618
        osutils.make_readonly(lock_path)
2872.5.1 by Martin Pool
Avoid internal error tracebacks on failure to lock on readonly transport (#129701).
619
        self.assertRaises(errors.LockFailed, ld1.attempt_lock)
2555.3.5 by Martin Pool
Return token directly from LockDir.acquire to avoid unnecessary peek()
620
621
    def test_lock_by_token(self):
622
        ld1 = self.get_lock()
623
        token = ld1.lock_write()
624
        self.assertNotEqual(None, token)
625
        ld2 = self.get_lock()
626
        t2 = ld2.lock_write(token)
627
        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)
628
629
    def test_lock_with_buggy_rename(self):
630
        # test that lock acquisition handles servers which pretend they
631
        # renamed correctly but that actually fail
632
        t = transport.get_transport('brokenrename+' + self.get_url())
633
        ld1 = LockDir(t, 'test_lock')
634
        ld1.create()
635
        ld1.attempt_lock()
636
        ld2 = LockDir(t, 'test_lock')
2555.3.14 by Martin Pool
Better handling in LockDir of rename that moves one directory within another
637
        # we should fail to lock
638
        e = self.assertRaises(errors.LockContention, ld2.attempt_lock)
639
        # now the original caller should succeed in unlocking
640
        ld1.unlock()
641
        # and there should be nothing left over
642
        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
643
644
    def test_failed_lock_leaves_no_trash(self):
645
        # if we fail to acquire the lock, we don't leave pending directories
646
        # behind -- https://bugs.launchpad.net/bzr/+bug/109169
647
        ld1 = self.get_lock()
648
        ld2 = self.get_lock()
649
        # should be nothing before we start
650
        ld1.create()
651
        t = self.get_transport().clone('test_lock')
652
        def check_dir(a):
653
            self.assertEquals(a, t.list_dir('.'))
654
        check_dir([])
655
        # when held, that's all we see
656
        ld1.attempt_lock()
657
        check_dir(['held'])
658
        # second guy should fail
659
        self.assertRaises(errors.LockContention, ld2.attempt_lock)
660
        # no kibble
661
        check_dir(['held'])
3331.3.1 by Robert Collins
* ``LockDir`` lock acquisition and release now trigger hooks allowing
662
663
    def record_hook(self, result):
664
        self._calls.append(result)
665
666
    def reset_hooks(self):
3724.1.1 by Martin Pool
Move Lock hooks onto a base Lock class and make them more consistent with other lock classes
667
        self._old_hooks = lock.Lock.hooks
3331.3.1 by Robert Collins
* ``LockDir`` lock acquisition and release now trigger hooks allowing
668
        self.addCleanup(self.restore_hooks)
3724.1.1 by Martin Pool
Move Lock hooks onto a base Lock class and make them more consistent with other lock classes
669
        lock.Lock.hooks = lock.LockHooks()
3331.3.1 by Robert Collins
* ``LockDir`` lock acquisition and release now trigger hooks allowing
670
671
    def restore_hooks(self):
3724.1.1 by Martin Pool
Move Lock hooks onto a base Lock class and make them more consistent with other lock classes
672
        lock.Lock.hooks = self._old_hooks
3331.3.1 by Robert Collins
* ``LockDir`` lock acquisition and release now trigger hooks allowing
673
3331.3.12 by Martin Pool
Remove PhysicalLock class
674
    def test_LockDir_acquired_success(self):
675
        # the LockDir.lock_acquired hook fires when a lock is acquired.
3331.3.1 by Robert Collins
* ``LockDir`` lock acquisition and release now trigger hooks allowing
676
        self._calls = []
677
        self.reset_hooks()
3331.3.11 by Martin Pool
Move LockDir hooks onto LockDir
678
        LockDir.hooks.install_named_hook('lock_acquired',
3331.3.9 by Martin Pool
Change one more case from using install_hook to install_named_hook
679
            self.record_hook, 'record_hook')
3331.3.1 by Robert Collins
* ``LockDir`` lock acquisition and release now trigger hooks allowing
680
        ld = self.get_lock()
681
        ld.create()
682
        self.assertEqual([], self._calls)
683
        result = ld.attempt_lock()
3331.3.2 by Robert Collins
Polish on lock hooks to be easier to use.
684
        lock_path = ld.transport.abspath(ld.path)
685
        self.assertEqual([lock.LockResult(lock_path, result)], self._calls)
3331.3.1 by Robert Collins
* ``LockDir`` lock acquisition and release now trigger hooks allowing
686
        ld.unlock()
3331.3.2 by Robert Collins
Polish on lock hooks to be easier to use.
687
        self.assertEqual([lock.LockResult(lock_path, result)], self._calls)
3331.3.1 by Robert Collins
* ``LockDir`` lock acquisition and release now trigger hooks allowing
688
3331.3.12 by Martin Pool
Remove PhysicalLock class
689
    def test_LockDir_acquired_fail(self):
690
        # the LockDir.lock_acquired hook does not fire on failure.
3331.3.1 by Robert Collins
* ``LockDir`` lock acquisition and release now trigger hooks allowing
691
        self._calls = []
692
        self.reset_hooks()
693
        ld = self.get_lock()
694
        ld.create()
695
        ld2 = self.get_lock()
696
        ld2.attempt_lock()
697
        # install a lock hook now, when the disk lock is locked
3331.3.11 by Martin Pool
Move LockDir hooks onto LockDir
698
        LockDir.hooks.install_named_hook('lock_acquired',
3331.3.8 by Martin Pool
Change from using install_hook to install_named_hook
699
            self.record_hook, 'record_hook')
3331.3.1 by Robert Collins
* ``LockDir`` lock acquisition and release now trigger hooks allowing
700
        self.assertRaises(errors.LockContention, ld.attempt_lock)
701
        self.assertEqual([], self._calls)
702
        ld2.unlock()
703
        self.assertEqual([], self._calls)
704
3331.3.12 by Martin Pool
Remove PhysicalLock class
705
    def test_LockDir_released_success(self):
706
        # the LockDir.lock_released hook fires when a lock is acquired.
3331.3.1 by Robert Collins
* ``LockDir`` lock acquisition and release now trigger hooks allowing
707
        self._calls = []
708
        self.reset_hooks()
3331.3.11 by Martin Pool
Move LockDir hooks onto LockDir
709
        LockDir.hooks.install_named_hook('lock_released',
3331.3.8 by Martin Pool
Change from using install_hook to install_named_hook
710
            self.record_hook, 'record_hook')
3331.3.1 by Robert Collins
* ``LockDir`` lock acquisition and release now trigger hooks allowing
711
        ld = self.get_lock()
712
        ld.create()
713
        self.assertEqual([], self._calls)
714
        result = ld.attempt_lock()
715
        self.assertEqual([], self._calls)
716
        ld.unlock()
3331.3.2 by Robert Collins
Polish on lock hooks to be easier to use.
717
        lock_path = ld.transport.abspath(ld.path)
718
        self.assertEqual([lock.LockResult(lock_path, result)], self._calls)
3331.3.1 by Robert Collins
* ``LockDir`` lock acquisition and release now trigger hooks allowing
719
3331.3.12 by Martin Pool
Remove PhysicalLock class
720
    def test_LockDir_released_fail(self):
721
        # the LockDir.lock_released hook does not fire on failure.
3331.3.1 by Robert Collins
* ``LockDir`` lock acquisition and release now trigger hooks allowing
722
        self._calls = []
723
        self.reset_hooks()
724
        ld = self.get_lock()
725
        ld.create()
726
        ld2 = self.get_lock()
727
        ld.attempt_lock()
728
        ld2.force_break(ld2.peek())
3331.3.11 by Martin Pool
Move LockDir hooks onto LockDir
729
        LockDir.hooks.install_named_hook('lock_released',
3331.3.8 by Martin Pool
Change from using install_hook to install_named_hook
730
            self.record_hook, 'record_hook')
3331.3.1 by Robert Collins
* ``LockDir`` lock acquisition and release now trigger hooks allowing
731
        self.assertRaises(LockBroken, ld.unlock)
732
        self.assertEqual([], self._calls)