190
184
# it should only take about 0.4 seconds, but we allow more time in
191
185
# case the machine is heavily loaded
192
186
self.assertTrue(after - before <= 8.0,
193
"took %f seconds to detect lock contention" % (after - before))
187
"took %f seconds to detect lock contention" % (after - before))
196
190
self.assertEqual(1, len(self._logged_reports))
197
self.assertContainsRe(self._logged_reports[0][0],
198
r'Unable to obtain lock .* held by jrandom@example\.com on .*'
199
r' \(process #\d+\), acquired .* ago\.\n'
200
r'Will continue to try until \d{2}:\d{2}:\d{2}, unless '
201
r'you press Ctrl-C.\n'
202
r'See "bzr help break-lock" for more.')
191
self.assertEqual(self._logged_reports[0][0],
192
'%s lock %s held by %s\n'
193
'at %s [process #%s], acquired %s.\n'
194
'Will continue to try until %s, unless '
195
'you press Ctrl-C.\n'
196
'See "bzr help break-lock" for more.')
197
start, lock_url, user, hostname, pid, time_ago, deadline_str = \
198
self._logged_reports[0][1]
199
self.assertEqual(start, u'Unable to obtain')
200
self.assertEqual(user, u'jrandom@example.com')
202
self.assertContainsRe(pid, r'\d+')
203
self.assertContainsRe(time_ago, r'.* ago')
204
self.assertContainsRe(deadline_str, r'\d{2}:\d{2}:\d{2}')
204
206
def test_31_lock_wait_easy(self):
205
207
"""Succeed when waiting on a lock with no contention.
421
417
self.assertFalse(t.has('test_lock/held/info'))
423
def test_display_form(self):
419
def test__format_lock_info(self):
424
420
ld1 = self.get_lock()
428
info_list = ld1.peek().to_readable_dict()
424
info_list = ld1._format_lock_info(ld1.peek())
431
self.assertEqual(info_list['user'], u'jrandom@example.com')
432
self.assertContainsRe(info_list['pid'], '^\d+$')
433
self.assertContainsRe(info_list['time_ago'], r'^\d+ seconds? ago$')
427
self.assertEqual(info_list[0], u'jrandom@example.com')
428
# info_list[1] is hostname. we skip this.
429
self.assertContainsRe(info_list[2], '^\d+$') # pid
430
self.assertContainsRe(info_list[3], r'^\d+ seconds? ago$') # time_ago
435
432
def test_lock_without_email(self):
436
global_config = config.GlobalStack()
433
global_config = config.GlobalConfig()
437
434
# Intentionally has no email address
438
global_config.set('email', 'User Identity')
435
global_config.set_user_option('email', 'User Identity')
439
436
ld1 = self.get_lock()
653
646
ld2.force_break(holder_info)
654
647
lock_path = ld.transport.abspath(ld.path)
655
648
self.assertEqual([], self._calls)
658
class TestLockHeldInfo(TestCaseInTempDir):
659
"""Can get information about the lock holder, and detect whether they're
663
info = LockHeldInfo.for_this_process(None)
664
self.assertContainsRe(repr(info), r"LockHeldInfo\(.*\)")
666
def test_unicode(self):
667
info = LockHeldInfo.for_this_process(None)
668
self.assertContainsRe(unicode(info),
669
r'held by .* on .* \(process #\d+\), acquired .* ago')
671
def test_is_locked_by_this_process(self):
672
info = LockHeldInfo.for_this_process(None)
673
self.assertTrue(info.is_locked_by_this_process())
675
def test_is_not_locked_by_this_process(self):
676
info = LockHeldInfo.for_this_process(None)
677
info.info_dict['pid'] = '123123123123123'
678
self.assertFalse(info.is_locked_by_this_process())
680
def test_lock_holder_live_process(self):
681
"""Detect that the holder (this process) is still running."""
682
info = LockHeldInfo.for_this_process(None)
683
self.assertFalse(info.is_lock_holder_known_dead())
685
def test_lock_holder_dead_process(self):
686
"""Detect that the holder (this process) is still running."""
687
self.overrideAttr(lockdir, 'get_host_name',
688
lambda: 'aproperhostname')
689
info = LockHeldInfo.for_this_process(None)
690
info.info_dict['pid'] = '123123123'
691
self.assertTrue(info.is_lock_holder_known_dead())
693
def test_lock_holder_other_machine(self):
694
"""The lock holder isn't here so we don't know if they're alive."""
695
info = LockHeldInfo.for_this_process(None)
696
info.info_dict['hostname'] = 'egg.example.com'
697
info.info_dict['pid'] = '123123123'
698
self.assertFalse(info.is_lock_holder_known_dead())
700
def test_lock_holder_other_user(self):
701
"""Only auto-break locks held by this user."""
702
info = LockHeldInfo.for_this_process(None)
703
info.info_dict['user'] = 'notme@example.com'
704
info.info_dict['pid'] = '123123123'
705
self.assertFalse(info.is_lock_holder_known_dead())
707
def test_no_good_hostname(self):
708
"""Correctly handle ambiguous hostnames.
710
If the lock's recorded with just 'localhost' we can't really trust
711
it's the same 'localhost'. (There are quite a few of them. :-)
712
So even if the process is known not to be alive, we can't say that's
715
self.overrideAttr(lockdir, 'get_host_name',
717
info = LockHeldInfo.for_this_process(None)
718
info.info_dict['pid'] = '123123123'
719
self.assertFalse(info.is_lock_holder_known_dead())
722
class TestStaleLockDir(TestCaseWithTransport):
723
"""Can automatically break stale locks.
725
:see: https://bugs.launchpad.net/bzr/+bug/220464
728
def test_auto_break_stale_lock(self):
729
"""Locks safely known to be stale are just cleaned up.
731
This generates a warning but no other user interaction.
733
self.overrideAttr(lockdir, 'get_host_name',
734
lambda: 'aproperhostname')
735
# This is off by default at present; see the discussion in the bug.
736
# If you change the default, don't forget to update the docs.
737
config.GlobalStack().set('locks.steal_dead', True)
738
# Create a lock pretending to come from a different nonexistent
739
# process on the same machine.
740
l1 = LockDir(self.get_transport(), 'a',
741
extra_holder_info={'pid': '12312313'})
742
token_1 = l1.attempt_lock()
743
l2 = LockDir(self.get_transport(), 'a')
744
token_2 = l2.attempt_lock()
745
# l1 will notice its lock was stolen.
746
self.assertRaises(errors.LockBroken,
750
def test_auto_break_stale_lock_configured_off(self):
751
"""Automatic breaking can be turned off"""
752
l1 = LockDir(self.get_transport(), 'a',
753
extra_holder_info={'pid': '12312313'})
754
token_1 = l1.attempt_lock()
755
self.addCleanup(l1.unlock)
756
l2 = LockDir(self.get_transport(), 'a')
757
# This fails now, because dead lock breaking is off by default.
758
self.assertRaises(LockContention,
760
# and it's in fact not broken