191
189
"took %f seconds to detect lock contention" % (after - before))
192
lock_base = lf2.transport.abspath(lf2.path)
194
193
self.assertEqual(1, len(self._logged_reports))
195
self.assertEqual(self._logged_reports[0][0],
196
'%s lock %s held by %s\n'
197
'at %s [process #%s], acquired %s.\n'
198
'Will continue to try until %s, unless '
199
'you press Ctrl-C.\n'
200
'See "bzr help break-lock" for more.')
201
start, lock_url, user, hostname, pid, time_ago, deadline_str = \
202
self._logged_reports[0][1]
203
self.assertEqual(start, u'Unable to obtain')
204
self.assertEqual(user, u'jrandom@example.com')
206
self.assertContainsRe(pid, r'\d+')
207
self.assertContainsRe(time_ago, r'.* ago')
208
self.assertContainsRe(deadline_str, r'\d{2}:\d{2}:\d{2}')
194
lock_url = lf2.transport.abspath(lf2.path)
195
self.assertEqual('%s %s\n'
197
'Will continue to try until %s, unless '
199
'If you\'re sure that it\'s not being '
200
'modified, use bzr break-lock %s',
201
self._logged_reports[0][0])
202
args = self._logged_reports[0][1]
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')
208
self.assertContainsRe(args[4], r'\d\d:\d\d:\d\d')
210
210
def test_31_lock_wait_easy(self):
211
211
"""Succeed when waiting on a lock with no contention.
557
553
# do this without IO redirection to ensure it doesn't prompt.
558
554
self.assertRaises(AssertionError, ld1.break_lock)
559
555
orig_factory = bzrlib.ui.ui_factory
560
bzrlib.ui.ui_factory = bzrlib.ui.CannedInputUIFactory([True])
556
# silent ui - no need for stdout
557
bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
558
bzrlib.ui.ui_factory.stdin = StringIO("y\n")
563
561
self.assertRaises(LockBroken, ld1.unlock)
565
563
bzrlib.ui.ui_factory = orig_factory
567
def test_break_lock_corrupt_info(self):
568
"""break_lock works even if the info file is corrupt (and tells the UI
572
ld2 = self.get_lock()
575
ld.transport.put_bytes_non_atomic('test_lock/held/info', '\0')
576
class LoggingUIFactory(bzrlib.ui.SilentUIFactory):
579
def get_boolean(self, prompt):
580
self.prompts.append(('boolean', prompt))
582
ui = LoggingUIFactory()
583
orig_factory = bzrlib.ui.ui_factory
584
bzrlib.ui.ui_factory = ui
587
self.assertLength(1, ui.prompts)
588
self.assertEqual('boolean', ui.prompts[0][0])
589
self.assertStartsWith(ui.prompts[0][1], 'Break (corrupt LockDir')
590
self.assertRaises(LockBroken, ld.unlock)
592
bzrlib.ui.ui_factory = orig_factory
594
def test_break_lock_missing_info(self):
595
"""break_lock works even if the info file is missing (and tells the UI
599
ld2 = self.get_lock()
602
ld.transport.delete('test_lock/held/info')
603
class LoggingUIFactory(bzrlib.ui.SilentUIFactory):
606
def get_boolean(self, prompt):
607
self.prompts.append(('boolean', prompt))
609
ui = LoggingUIFactory()
610
orig_factory = bzrlib.ui.ui_factory
611
bzrlib.ui.ui_factory = ui
614
self.assertRaises(LockBroken, ld.unlock)
615
self.assertLength(0, ui.prompts)
617
bzrlib.ui.ui_factory = orig_factory
618
# Suppress warnings due to ld not being unlocked
619
# XXX: if lock_broken hook was invoked in this case, this hack would
620
# not be necessary. - Andrew Bennetts, 2010-09-06.
621
del self._lock_actions[:]
623
565
def test_create_missing_base_directory(self):
624
566
"""If LockDir.path doesn't exist, it can be created
652
594
info_list = ld1._format_lock_info(ld1.peek())
655
self.assertEqual(info_list[0], u'jrandom@example.com')
656
# info_list[1] is hostname. we skip this.
657
self.assertContainsRe(info_list[2], '^\d+$') # pid
658
self.assertContainsRe(info_list[3], r'^\d+ seconds? ago$') # time_ago
597
self.assertEqual('lock %s' % (ld1.transport.abspath(ld1.path),),
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$')
660
603
def test_lock_without_email(self):
661
604
global_config = config.GlobalConfig()
715
655
# when held, that's all we see
716
656
ld1.attempt_lock()
717
self.addCleanup(ld1.unlock)
718
657
check_dir(['held'])
719
658
# second guy should fail
720
659
self.assertRaises(errors.LockContention, ld2.attempt_lock)
722
661
check_dir(['held'])
724
def test_no_lockdir_info(self):
725
"""We can cope with empty info files."""
726
# This seems like a fairly common failure case - see
727
# <https://bugs.launchpad.net/bzr/+bug/185103> and all its dupes.
728
# Processes are often interrupted after opening the file
729
# before the actual contents are committed.
730
t = self.get_transport()
732
t.mkdir('test_lock/held')
733
t.put_bytes('test_lock/held/info', '')
734
lf = LockDir(t, 'test_lock')
736
formatted_info = lf._format_lock_info(info)
738
['<unknown>', '<unknown>', '<unknown>', '(unknown)'],
741
def test_corrupt_lockdir_info(self):
742
"""We can cope with corrupt (and thus unparseable) info files."""
743
# This seems like a fairly common failure case too - see
744
# <https://bugs.edge.launchpad.net/bzr/+bug/619872> for instance.
745
# In particular some systems tend to fill recently created files with
746
# nul bytes after recovering from a system crash.
747
t = self.get_transport()
749
t.mkdir('test_lock/held')
750
t.put_bytes('test_lock/held/info', '\0')
751
lf = LockDir(t, 'test_lock')
752
self.assertRaises(errors.LockCorrupt, lf.peek)
753
# Currently attempt_lock gives LockContention, but LockCorrupt would be
754
# a reasonable result too.
756
(errors.LockCorrupt, errors.LockContention), lf.attempt_lock)
757
self.assertRaises(errors.LockCorrupt, lf.validate_token, 'fake token')
759
def test_missing_lockdir_info(self):
760
"""We can cope with absent info files."""
761
t = self.get_transport()
763
t.mkdir('test_lock/held')
764
lf = LockDir(t, 'test_lock')
765
# In this case we expect the 'not held' result from peek, because peek
766
# cannot be expected to notice that there is a 'held' directory with no
768
self.assertEqual(None, lf.peek())
769
# And lock/unlock may work or give LockContention (but not any other
773
except LockContention:
774
# LockContention is ok, and expected on Windows
777
# no error is ok, and expected on POSIX (because POSIX allows
778
# os.rename over an empty directory).
780
# Currently raises TokenMismatch, but LockCorrupt would be reasonable
783
(errors.TokenMismatch, errors.LockCorrupt),
784
lf.validate_token, 'fake token')
787
class TestLockDirHooks(TestCaseWithTransport):
790
super(TestLockDirHooks, self).setUp()
794
return LockDir(self.get_transport(), 'test_lock')
796
663
def record_hook(self, result):
797
664
self._calls.append(result)
666
def reset_hooks(self):
667
self._old_hooks = lock.Lock.hooks
668
self.addCleanup(self.restore_hooks)
669
lock.Lock.hooks = lock.LockHooks()
671
def restore_hooks(self):
672
lock.Lock.hooks = self._old_hooks
799
674
def test_LockDir_acquired_success(self):
800
675
# the LockDir.lock_acquired hook fires when a lock is acquired.
801
678
LockDir.hooks.install_named_hook('lock_acquired',
802
self.record_hook, 'record_hook')
679
self.record_hook, 'record_hook')
803
680
ld = self.get_lock()
805
682
self.assertEqual([], self._calls)
839
720
def test_LockDir_released_fail(self):
840
721
# the LockDir.lock_released hook does not fire on failure.
841
724
ld = self.get_lock()
843
726
ld2 = self.get_lock()
844
727
ld.attempt_lock()
845
728
ld2.force_break(ld2.peek())
846
729
LockDir.hooks.install_named_hook('lock_released',
847
self.record_hook, 'record_hook')
730
self.record_hook, 'record_hook')
848
731
self.assertRaises(LockBroken, ld.unlock)
849
732
self.assertEqual([], self._calls)
851
def test_LockDir_broken_success(self):
852
# the LockDir.lock_broken hook fires when a lock is broken.
855
ld2 = self.get_lock()
856
result = ld.attempt_lock()
857
LockDir.hooks.install_named_hook('lock_broken',
858
self.record_hook, 'record_hook')
859
ld2.force_break(ld2.peek())
860
lock_path = ld.transport.abspath(ld.path)
861
self.assertEqual([lock.LockResult(lock_path, result)], self._calls)
863
def test_LockDir_broken_failure(self):
864
# the LockDir.lock_broken hook does not fires when a lock is already
868
ld2 = self.get_lock()
869
result = ld.attempt_lock()
870
holder_info = ld2.peek()
872
LockDir.hooks.install_named_hook('lock_broken',
873
self.record_hook, 'record_hook')
874
ld2.force_break(holder_info)
875
lock_path = ld.transport.abspath(ld.path)
876
self.assertEqual([], self._calls)