~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/branch_implementations/test_locking.py

Implement RemoteBranch.lock_write/unlock as smart operations.

Because Branch.lock_write/unlock actually also lock/unlock the repository, I've
slightly changed lock_write's interface to accept and return 'tokens' rather
than 'token'.  i.e. a 2-tuple of (branch token, repo token), or None.

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
"""Test locks across all branch implemenations"""
18
18
 
19
19
from bzrlib import errors
20
 
from bzrlib import lockdir
21
20
from bzrlib.tests.branch_implementations.test_branch import TestCaseWithBranch
22
21
from bzrlib.tests.lock_helpers import TestPreventLocking, LockWrapper
23
22
 
26
25
 
27
26
    def setUp(self):
28
27
        TestCaseWithBranch.setUp(self)
29
 
        # Reduce the default timeout, so that if tests fail, they will do so
30
 
        # reasonably quickly.
31
 
        orig_timeout = lockdir._DEFAULT_TIMEOUT_SECONDS
32
 
        def resetTimeout():
33
 
            lockdir._DEFAULT_TIMEOUT_SECONDS = orig_timeout
34
 
        self.addCleanup(resetTimeout)
35
 
        lockdir._DEFAULT_TIMEOUT_SECONDS = 3
 
28
        self.reduceLockdirTimeout()
36
29
 
37
30
    def get_instrumented_branch(self):
38
31
        """Get a Branch object which has been instrumented"""
232
225
                          ('rc', 'ul', True),
233
226
                         ], self.locks)
234
227
 
235
 
    def test_lock_write_returns_None_refuses_token(self):
 
228
    def test_lock_write_returns_None_refuses_tokens(self):
236
229
        branch = self.make_branch('b')
237
 
        token = branch.lock_write()
 
230
        tokens = branch.lock_write()
238
231
        try:
239
 
            if token is not None:
 
232
            if tokens is not None:
240
233
                # This test does not apply, because this lockable supports
241
234
                # tokens.
242
235
                return
243
236
            self.assertRaises(errors.TokenLockingNotSupported,
244
 
                              branch.lock_write, token='token')
 
237
                              branch.lock_write, tokens=('token','token'))
245
238
        finally:
246
239
            branch.unlock()
247
240
 
248
 
    def test_lock_write_raises_on_token_mismatch(self):
 
241
    def test_reentering_lock_write_raises_on_token_mismatch(self):
249
242
        branch = self.make_branch('b')
250
 
        token = branch.lock_write()
 
243
        tokens = branch.lock_write()
251
244
        try:
252
 
            if token is None:
 
245
            if tokens is None:
253
246
                # This test does not apply, because this lockable refuses
254
247
                # tokens.
255
248
                return
256
 
            different_token = token + 'xxx'
257
 
            # Re-using the same lockable instance with a different token will
258
 
            # raise TokenMismatch.
259
 
            self.assertRaises(errors.TokenMismatch,
260
 
                              branch.lock_write, token=different_token)
261
 
            # A seperate instance for the same lockable will also raise
262
 
            # TokenMismatch.
263
 
            # This detects the case where a caller claims to have a lock (via
264
 
            # the token) for an external resource, but doesn't (the token is
265
 
            # different).  Clients need a seperate lock object to make sure the
266
 
            # external resource is probed, whereas the existing lock object
267
 
            # might cache.
 
249
            branch_token, repo_token = tokens
 
250
            different_branch_token = branch_token + 'xxx'
 
251
            different_repo_token = repo_token + 'xxx'
 
252
            # Re-using the same lockable instance with a different branch token
 
253
            # will raise TokenMismatch.
 
254
            self.assertRaises(errors.TokenMismatch,
 
255
                              branch.lock_write,
 
256
                              tokens=(different_branch_token, repo_token))
 
257
            # Similarly for a different repository token.
 
258
            self.assertRaises(errors.TokenMismatch,
 
259
                              branch.lock_write,
 
260
                              tokens=(branch_token, different_repo_token))
 
261
        finally:
 
262
            branch.unlock()
 
263
 
 
264
    def test_lock_write_with_nonmatching_token(self):
 
265
        branch = self.make_branch('b')
 
266
        tokens = branch.lock_write()
 
267
        try:
 
268
            if tokens is None:
 
269
                # This test does not apply, because this branch refuses
 
270
                # tokens.
 
271
                return
 
272
            branch_token, repo_token = tokens
 
273
            different_branch_token = branch_token + 'xxx'
 
274
            different_repo_token = repo_token + 'xxx'
 
275
 
268
276
            new_branch = branch.bzrdir.open_branch()
269
277
            # We only want to test the relocking abilities of branch, so use the
270
278
            # existing repository object which is already locked.
271
279
            new_branch.repository = branch.repository
272
280
            self.assertRaises(errors.TokenMismatch,
273
 
                              new_branch.lock_write, token=different_token)
 
281
                              new_branch.lock_write,
 
282
                              tokens=(different_branch_token, repo_token))
 
283
            self.assertRaises(errors.TokenMismatch,
 
284
                              new_branch.lock_write,
 
285
                              tokens=(branch_token, different_repo_token))
274
286
        finally:
275
287
            branch.unlock()
276
288
 
 
289
 
277
290
    def test_lock_write_with_matching_token(self):
278
291
        """Test that a branch can be locked with a token, if it is already
279
292
        locked by that token."""
280
293
        branch = self.make_branch('b')
281
 
        token = branch.lock_write()
 
294
        tokens = branch.lock_write()
282
295
        try:
283
 
            if token is None:
 
296
            if tokens is None:
284
297
                # This test does not apply, because this branch refuses tokens.
285
298
                return
286
299
            # The same instance will accept a second lock_write if the specified
287
300
            # token matches.
288
 
            branch.lock_write(token=token)
 
301
            branch.lock_write(tokens=tokens)
289
302
            branch.unlock()
290
303
            # Calling lock_write on a new instance for the same lockable will
291
304
            # also succeed.
293
306
            # We only want to test the relocking abilities of branch, so use the
294
307
            # existing repository object which is already locked.
295
308
            new_branch.repository = branch.repository
296
 
            new_branch.lock_write(token=token)
 
309
            new_branch.lock_write(tokens=tokens)
297
310
            new_branch.unlock()
298
311
        finally:
299
312
            branch.unlock()
300
313
 
301
 
    def test_unlock_after_lock_write_with_token(self):
 
314
    def test_unlock_after_lock_write_with_tokens(self):
302
315
        # If lock_write did not physically acquire the lock (because it was
303
 
        # passed a token), then unlock should not physically release it.
 
316
        # passed some tokens), then unlock should not physically release it.
304
317
        branch = self.make_branch('b')
305
 
        token = branch.lock_write()
 
318
        tokens = branch.lock_write()
306
319
        try:
307
 
            if token is None:
 
320
            if tokens is None:
308
321
                # This test does not apply, because this lockable refuses
309
322
                # tokens.
310
323
                return
312
325
            # We only want to test the relocking abilities of branch, so use the
313
326
            # existing repository object which is already locked.
314
327
            new_branch.repository = branch.repository
315
 
            new_branch.lock_write(token=token)
 
328
            new_branch.lock_write(tokens=tokens)
316
329
            new_branch.unlock()
317
330
            self.assertTrue(branch.get_physical_lock_status()) #XXX
318
331
        finally:
319
332
            branch.unlock()
320
333
 
321
 
    def test_lock_write_with_token_fails_when_unlocked(self):
322
 
        # Lock and unlock to get a superficially valid token.  This mimics a
 
334
    def test_lock_write_with_tokens_fails_when_unlocked(self):
 
335
        # Lock and unlock to get superficially valid tokens.  This mimics a
323
336
        # likely programming error, where a caller accidentally tries to lock
324
 
        # with a token that is no longer valid (because the original lock was
 
337
        # with tokens that are no longer valid (because the original lock was
325
338
        # released).
326
339
        branch = self.make_branch('b')
327
 
        token = branch.lock_write()
 
340
        tokens = branch.lock_write()
328
341
        branch.unlock()
329
 
        if token is None:
 
342
        if tokens is None:
330
343
            # This test does not apply, because this lockable refuses
331
344
            # tokens.
332
345
            return
333
346
 
334
347
        self.assertRaises(errors.TokenMismatch,
335
 
                          branch.lock_write, token=token)
 
348
                          branch.lock_write, tokens=tokens)
336
349
 
337
 
    def test_lock_write_reenter_with_token(self):
 
350
    def test_lock_write_reenter_with_tokens(self):
338
351
        branch = self.make_branch('b')
339
 
        token = branch.lock_write()
 
352
        tokens = branch.lock_write()
340
353
        try:
341
 
            if token is None:
 
354
            if tokens is None:
342
355
                # This test does not apply, because this lockable refuses
343
356
                # tokens.
344
357
                return
345
358
            # Relock with a token.
346
 
            branch.lock_write(token=token)
 
359
            branch.lock_write(tokens=tokens)
347
360
            branch.unlock()
348
361
        finally:
349
362
            branch.unlock()
354
367
        new_branch.lock_write()
355
368
        new_branch.unlock()
356
369
 
 
370
    def test_leave_lock_in_place(self):
 
371
        branch = self.make_branch('b')
 
372
        # Lock the branch, then use leave_lock_in_place so that when we
 
373
        # unlock the branch the lock is still held on disk.
 
374
        tokens = branch.lock_write()
 
375
        try:
 
376
            if tokens is None:
 
377
                # This test does not apply, because this repository refuses lock
 
378
                # tokens.
 
379
                self.assertRaises(NotImplementedError, branch.leave_lock_in_place)
 
380
                return
 
381
            branch.leave_lock_in_place()
 
382
        finally:
 
383
            branch.unlock()
 
384
        # We should be unable to relock the repo.
 
385
        self.assertRaises(errors.LockContention, branch.lock_write)
 
386
 
 
387
    def test_dont_leave_lock_in_place(self):
 
388
        branch = self.make_branch('b')
 
389
        # Create a lock on disk.
 
390
        tokens = branch.lock_write()
 
391
        try:
 
392
            if tokens is None:
 
393
                # This test does not apply, because this branch refuses lock
 
394
                # tokens.
 
395
                self.assertRaises(NotImplementedError,
 
396
                                  branch.dont_leave_lock_in_place)
 
397
                return
 
398
            try:
 
399
                branch.leave_lock_in_place()
 
400
            except NotImplementedError:
 
401
                # This branch doesn't support this API.
 
402
                return
 
403
            branch.repository.leave_lock_in_place()
 
404
        finally:
 
405
            branch.unlock()
 
406
        # Reacquire the lock (with a different branch object) by using the
 
407
        # tokens.
 
408
        new_branch = branch.bzrdir.open_branch()
 
409
        new_branch.lock_write(tokens=tokens)
 
410
        # Call dont_leave_lock_in_place, so that the lock will be released by
 
411
        # this instance, even though the lock wasn't originally acquired by it.
 
412
        new_branch.dont_leave_lock_in_place()
 
413
        new_branch.repository.dont_leave_lock_in_place()
 
414
        new_branch.unlock()
 
415
        # Now the branch is unlocked.  Test this by locking it (without tokens).
 
416
        branch.lock_write()
 
417
        branch.unlock()
 
418
 
357
419