13
13
# You should have received a copy of the GNU General Public License
14
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
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
"""Tests for LockDir"""
161
162
lf2 = LockDir(self.get_readonly_transport(), 'test_lock')
162
163
self.assertEqual(lf2.peek(), None)
163
164
lf1.attempt_lock()
165
self.addCleanup(lf1.unlock)
164
166
info2 = lf2.peek()
165
167
self.assertTrue(info2)
166
168
self.assertEqual(info2['nonce'], lf1.nonce)
168
170
def test_30_lock_wait_fail(self):
169
171
"""Wait on a lock, then fail
171
173
We ask to wait up to 400ms; this should fail within at most one
172
174
second. (Longer times are more realistic but we don't want the test
173
175
suite to take too long, and this should do for now.)
185
187
after = time.time()
186
188
# it should only take about 0.4 seconds, but we allow more time in
187
189
# case the machine is heavily loaded
188
self.assertTrue(after - before <= 8.0,
190
self.assertTrue(after - before <= 8.0,
189
191
"took %f seconds to detect lock contention" % (after - before))
192
lock_base = lf2.transport.abspath(lf2.path)
193
194
self.assertEqual(1, len(self._logged_reports))
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')
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}')
210
210
def test_31_lock_wait_easy(self):
211
211
"""Succeed when waiting on a lock with no contention.
277
277
self.assertContainsRe(args[4], r'\d\d:\d\d:\d\d')
279
279
def test_34_lock_write_waits(self):
280
"""LockDir.lock_write() will wait for the lock."""
280
"""LockDir.lock_write() will wait for the lock."""
281
281
# the test suite sets the default to 0 to make deadlocks fail fast.
282
282
# change it for this test, as we want to try a manual deadlock.
283
283
raise tests.TestSkipped('Timing-sensitive test')
321
321
def test_35_wait_lock_changing(self):
322
322
"""LockDir.wait_lock() will report if the lock changes underneath.
324
324
This is the stages we want to happen:
326
326
0) Synchronization locks are created and locked.
327
327
1) Lock1 obtains the lockdir, and releases the 'check' lock.
328
328
2) Lock2 grabs the 'check' lock, and checks the lockdir.
329
It sees the lockdir is already acquired, reports the fact,
329
It sees the lockdir is already acquired, reports the fact,
330
330
and unsets the 'checked' lock.
331
331
3) Thread1 blocks on acquiring the 'checked' lock, and then tells
332
332
Lock1 to release and acquire the lockdir. This resets the 'check'
334
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
335
that the holder of the lock has changed, and so reports a new
337
337
5) Thread1 blocks on the 'checked' lock, this time, it completely
338
338
unlocks the lockdir, allowing Lock2 to acquire the lock.
418
418
self.assertEqual('%s %s\n'
420
420
'Will continue to try until %s, unless '
422
'If you\'re sure that it\'s not being '
423
'modified, use bzr break-lock %s',
421
'you press Ctrl-C.\n'
422
'See "bzr help break-lock" for more.',
424
423
self._logged_reports[0][0])
425
424
args = self._logged_reports[0][1]
426
425
self.assertEqual('Unable to obtain', args[0])
433
432
self.assertEqual('%s %s\n'
435
434
'Will continue to try until %s, unless '
437
'If you\'re sure that it\'s not being '
438
'modified, use bzr break-lock %s',
435
'you press Ctrl-C.\n'
436
'See "bzr help break-lock" for more.',
439
437
self._logged_reports[1][0])
440
438
args = self._logged_reports[1][1]
441
439
self.assertEqual('Lock owner changed for', args[0])
553
557
# do this without IO redirection to ensure it doesn't prompt.
554
558
self.assertRaises(AssertionError, ld1.break_lock)
555
559
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")
560
bzrlib.ui.ui_factory = bzrlib.ui.CannedInputUIFactory([True])
561
563
self.assertRaises(LockBroken, ld1.unlock)
594
596
info_list = ld1._format_lock_info(ld1.peek())
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$')
599
self.assertEqual(info_list[0], u'jrandom@example.com')
600
# info_list[1] is hostname. we skip this.
601
self.assertContainsRe(info_list[2], '^\d+$') # pid
602
self.assertContainsRe(info_list[3], r'^\d+ seconds? ago$') # time_ago
603
604
def test_lock_without_email(self):
604
605
global_config = config.GlobalConfig()
621
622
def test_lock_by_token(self):
622
623
ld1 = self.get_lock()
623
624
token = ld1.lock_write()
625
self.addCleanup(ld1.unlock)
624
626
self.assertNotEqual(None, token)
625
627
ld2 = self.get_lock()
626
628
t2 = ld2.lock_write(token)
629
self.addCleanup(ld2.unlock)
627
630
self.assertEqual(token, t2)
629
632
def test_lock_with_buggy_rename(self):
655
658
# when held, that's all we see
656
659
ld1.attempt_lock()
660
self.addCleanup(ld1.unlock)
657
661
check_dir(['held'])
658
662
# second guy should fail
659
663
self.assertRaises(errors.LockContention, ld2.attempt_lock)
661
665
check_dir(['held'])
667
def test_no_lockdir_info(self):
668
"""We can cope with empty info files."""
669
# This seems like a fairly common failure case - see
670
# <https://bugs.launchpad.net/bzr/+bug/185103> and all its dupes.
671
# Processes are often interrupted after opening the file
672
# before the actual contents are committed.
673
t = self.get_transport()
675
t.mkdir('test_lock/held')
676
t.put_bytes('test_lock/held/info', '')
677
lf = LockDir(t, 'test_lock')
679
formatted_info = lf._format_lock_info(info)
681
['<unknown>', '<unknown>', '<unknown>', '(unknown)'],
685
class TestLockDirHooks(TestCaseWithTransport):
688
super(TestLockDirHooks, self).setUp()
692
return LockDir(self.get_transport(), 'test_lock')
663
694
def record_hook(self, result):
664
695
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
674
697
def test_LockDir_acquired_success(self):
675
698
# the LockDir.lock_acquired hook fires when a lock is acquired.
678
699
LockDir.hooks.install_named_hook('lock_acquired',
679
self.record_hook, 'record_hook')
700
self.record_hook, 'record_hook')
680
701
ld = self.get_lock()
682
703
self.assertEqual([], self._calls)
689
710
def test_LockDir_acquired_fail(self):
690
711
# the LockDir.lock_acquired hook does not fire on failure.
693
712
ld = self.get_lock()
695
714
ld2 = self.get_lock()
696
715
ld2.attempt_lock()
697
716
# install a lock hook now, when the disk lock is locked
698
717
LockDir.hooks.install_named_hook('lock_acquired',
699
self.record_hook, 'record_hook')
718
self.record_hook, 'record_hook')
700
719
self.assertRaises(errors.LockContention, ld.attempt_lock)
701
720
self.assertEqual([], self._calls)
705
724
def test_LockDir_released_success(self):
706
725
# the LockDir.lock_released hook fires when a lock is acquired.
709
726
LockDir.hooks.install_named_hook('lock_released',
710
self.record_hook, 'record_hook')
727
self.record_hook, 'record_hook')
711
728
ld = self.get_lock()
713
730
self.assertEqual([], self._calls)
720
737
def test_LockDir_released_fail(self):
721
738
# the LockDir.lock_released hook does not fire on failure.
724
739
ld = self.get_lock()
726
741
ld2 = self.get_lock()
727
742
ld.attempt_lock()
728
743
ld2.force_break(ld2.peek())
729
744
LockDir.hooks.install_named_hook('lock_released',
730
self.record_hook, 'record_hook')
745
self.record_hook, 'record_hook')
731
746
self.assertRaises(LockBroken, ld.unlock)
732
747
self.assertEqual([], self._calls)
749
def test_LockDir_broken_success(self):
750
# the LockDir.lock_broken hook fires when a lock is broken.
753
ld2 = self.get_lock()
754
result = ld.attempt_lock()
755
LockDir.hooks.install_named_hook('lock_broken',
756
self.record_hook, 'record_hook')
757
ld2.force_break(ld2.peek())
758
lock_path = ld.transport.abspath(ld.path)
759
self.assertEqual([lock.LockResult(lock_path, result)], self._calls)
761
def test_LockDir_broken_failure(self):
762
# the LockDir.lock_broken hook does not fires when a lock is already
766
ld2 = self.get_lock()
767
result = ld.attempt_lock()
768
holder_info = ld2.peek()
770
LockDir.hooks.install_named_hook('lock_broken',
771
self.record_hook, 'record_hook')
772
ld2.force_break(holder_info)
773
lock_path = ld.transport.abspath(ld.path)
774
self.assertEqual([], self._calls)