284
def test_34_lock_write_waits(self):
285
"""LockDir.lock_write() will wait for the lock."""
286
t = self.get_transport()
287
lf1 = LockDir(t, 'test_lock')
291
def wait_and_unlock():
294
unlocker = Thread(target=wait_and_unlock)
297
lf2 = LockDir(t, 'test_lock')
298
self.setup_log_reporter(lf2)
306
# There should be only 1 report, even though it should have to
308
lock_base = lf2.transport.abspath(lf2.path)
309
self.assertEqual(1, len(self._logged_reports))
310
self.assertEqual('%s %s\n'
312
'Will continue to try until %s\n',
313
self._logged_reports[0][0])
314
args = self._logged_reports[0][1]
315
self.assertEqual('Unable to obtain', args[0])
316
self.assertEqual('lock %s' % (lock_base,), args[1])
317
self.assertStartsWith(args[2], 'held by ')
318
self.assertStartsWith(args[3], 'locked ')
319
self.assertEndsWith(args[3], ' ago')
320
self.assertContainsRe(args[4], r'\d\d:\d\d:\d\d')
322
def test_35_wait_lock_changing(self):
323
"""LockDir.wait_lock() will report if the lock changes underneath.
325
This is the stages we want to happen:
327
0) Synchronization locks are created and locked.
328
1) Lock1 obtains the lockdir, and releases the 'check' lock.
329
2) Lock2 grabs the 'check' lock, and checks the lockdir.
330
It sees the lockdir is already acquired, reports the fact,
331
and unsets the 'checked' lock.
332
3) Thread1 blocks on acquiring the 'checked' lock, and then tells
333
Lock1 to release and acquire the lockdir. This resets the 'check'
335
4) Lock2 acquires the 'check' lock, and checks again. It notices
336
that the holder of the lock has changed, and so reports a new
338
5) Thread1 blocks on the 'checked' lock, this time, it completely
339
unlocks the lockdir, allowing Lock2 to acquire the lock.
342
wait_to_check_lock = Lock()
343
wait_until_checked_lock = Lock()
345
wait_to_check_lock.acquire()
346
wait_until_checked_lock.acquire()
347
note('locked check and checked locks')
349
class LockDir1(LockDir):
350
"""Use the synchronization points for the first lock."""
352
def attempt_lock(self):
353
# Once we have acquired the lock, it is okay for
354
# the other lock to check it
356
return super(LockDir1, self).attempt_lock()
358
note('lock1: releasing check lock')
359
wait_to_check_lock.release()
361
class LockDir2(LockDir):
362
"""Use the synchronization points for the second lock."""
364
def attempt_lock(self):
365
note('lock2: waiting for check lock')
366
wait_to_check_lock.acquire()
367
note('lock2: acquired check lock')
369
return super(LockDir2, self).attempt_lock()
371
note('lock2: releasing checked lock')
372
wait_until_checked_lock.release()
374
t = self.get_transport()
375
lf1 = LockDir1(t, 'test_lock')
378
lf2 = LockDir2(t, 'test_lock')
379
self.setup_log_reporter(lf2)
381
def wait_and_switch():
383
# Block until lock2 has had a chance to check
384
note('lock1: waiting 1 for checked lock')
385
wait_until_checked_lock.acquire()
386
note('lock1: acquired for checked lock')
387
note('lock1: released lockdir')
389
note('lock1: acquiring lockdir')
390
# Create a new nonce, so the lock looks different.
391
lf1.nonce = osutils.rand_chars(20)
393
note('lock1: acquired lockdir')
395
# Block until lock2 has peeked again
396
note('lock1: waiting 2 for checked lock')
397
wait_until_checked_lock.acquire()
398
note('lock1: acquired for checked lock')
399
# Now unlock, and let lock 2 grab the lock
401
wait_to_check_lock.release()
403
unlocker = Thread(target=wait_and_switch)
406
# Wait and play against the other thread
407
lf2.wait_lock(timeout=1.0, poll=0.01)
412
# There should be 2 reports, because the lock changed
413
lock_base = lf2.transport.abspath(lf2.path)
414
self.assertEqual(2, len(self._logged_reports))
416
self.assertEqual('%s %s\n'
418
'Will continue to try until %s\n',
419
self._logged_reports[0][0])
420
args = self._logged_reports[0][1]
421
self.assertEqual('Unable to obtain', args[0])
422
self.assertEqual('lock %s' % (lock_base,), args[1])
423
self.assertStartsWith(args[2], 'held by ')
424
self.assertStartsWith(args[3], 'locked ')
425
self.assertEndsWith(args[3], ' ago')
426
self.assertContainsRe(args[4], r'\d\d:\d\d:\d\d')
428
self.assertEqual('%s %s\n'
430
'Will continue to try until %s\n',
431
self._logged_reports[1][0])
432
args = self._logged_reports[1][1]
433
self.assertEqual('Lock owner changed for', args[0])
434
self.assertEqual('lock %s' % (lock_base,), args[1])
435
self.assertStartsWith(args[2], 'held by ')
436
self.assertStartsWith(args[3], 'locked ')
437
self.assertEndsWith(args[3], ' ago')
438
self.assertContainsRe(args[4], r'\d\d:\d\d:\d\d')
239
440
def test_40_confirm_easy(self):
240
441
"""Confirm a lock that's already held"""
241
442
t = self.get_transport()
352
553
self.assertRaises(LockBroken, ld1.unlock)
354
555
bzrlib.ui.ui_factory = orig_factory
557
def test_create_missing_base_directory(self):
558
"""If LockDir.path doesn't exist, it can be created
560
Some people manually remove the entire lock/ directory trying
561
to unlock a stuck repository/branch/etc. Rather than failing
562
after that, just create the lock directory when needed.
564
t = self.get_transport()
565
lf1 = LockDir(t, 'test_lock')
568
self.failUnless(t.has('test_lock'))
571
self.failIf(t.has('test_lock'))
573
# This will create 'test_lock' if it needs to
575
self.failUnless(t.has('test_lock'))
576
self.failUnless(t.has('test_lock/held/info'))
579
self.failIf(t.has('test_lock/held/info'))
581
def test__format_lock_info(self):
582
ld1 = self.get_lock()
586
info_list = ld1._format_lock_info(ld1.peek())
589
self.assertEqual('lock %s' % (ld1.transport.abspath(ld1.path),),
591
self.assertContainsRe(info_list[1],
592
r'^held by .* on host .* \[process #\d*\]$')
593
self.assertContainsRe(info_list[2], r'locked \d+ seconds? ago$')
595
def test_lock_without_email(self):
596
global_config = config.GlobalConfig()
597
# Intentionally has no email address
598
global_config.set_user_option('email', 'User Identity')
599
ld1 = self.get_lock()