~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_lockdir.py

Abbreviate pack_stat struct format to '>6L'

Show diffs side-by-side

added added

removed removed

Lines of Context:
24
24
    config,
25
25
    errors,
26
26
    lock,
 
27
    lockdir,
27
28
    osutils,
28
29
    tests,
29
30
    transport,
35
36
    LockFailed,
36
37
    LockNotHeld,
37
38
    )
38
 
from bzrlib.lockdir import LockDir
 
39
from bzrlib.lockdir import (
 
40
    LockDir,
 
41
    LockHeldInfo,
 
42
    )
39
43
from bzrlib.tests import (
40
44
    features,
 
45
    TestCase,
41
46
    TestCaseWithTransport,
42
47
    )
43
 
from bzrlib.trace import note
44
48
 
45
49
# These tests are run on the default transport provided by the test framework
46
50
# (typically a local disk transport).  That can be changed by the --transport
48
52
# implementation are tested separately.  (The main requirement is just that
49
53
# they don't allow overwriting nonempty directories.)
50
54
 
 
55
 
51
56
class TestLockDir(TestCaseWithTransport):
52
57
    """Test LockDir operations"""
53
58
 
141
146
        self.addCleanup(lf1.unlock)
142
147
        # lock is held, should get some info on it
143
148
        info1 = lf1.peek()
144
 
        self.assertEqual(set(info1.keys()),
145
 
                         set(['user', 'nonce', 'hostname', 'pid', 'start_time']))
 
149
        self.assertEqual(set(info1.info_dict.keys()),
 
150
            set(['user', 'nonce', 'hostname', 'pid', 'start_time']))
146
151
        # should get the same info if we look at it through a different
147
152
        # instance
148
153
        info2 = LockDir(t, 'test_lock').peek()
161
166
        self.addCleanup(lf1.unlock)
162
167
        info2 = lf2.peek()
163
168
        self.assertTrue(info2)
164
 
        self.assertEqual(info2['nonce'], lf1.nonce)
 
169
        self.assertEqual(info2.get('nonce'), lf1.nonce)
165
170
 
166
171
    def test_30_lock_wait_fail(self):
167
172
        """Wait on a lock, then fail
184
189
            # it should only take about 0.4 seconds, but we allow more time in
185
190
            # case the machine is heavily loaded
186
191
            self.assertTrue(after - before <= 8.0,
187
 
                    "took %f seconds to detect lock contention" % (after - before))
 
192
                "took %f seconds to detect lock contention" % (after - before))
188
193
        finally:
189
194
            lf1.unlock()
190
195
        self.assertEqual(1, len(self._logged_reports))
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')
201
 
        # skip hostname
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}')
 
196
        self.assertContainsRe(self._logged_reports[0][0],
 
197
            r'Unable to obtain lock .* held by jrandom@example\.com on .*'
 
198
            r' \(process #\d+\), acquired .* ago\.\n'
 
199
            r'Will continue to try until \d{2}:\d{2}:\d{2}, unless '
 
200
            r'you press Ctrl-C.\n'
 
201
            r'See "bzr help break-lock" for more.')
205
202
 
206
203
    def test_31_lock_wait_easy(self):
207
204
        """Succeed when waiting on a lock with no contention.
349
346
        ld.create()
350
347
        ld.lock_write()
351
348
        ld.transport.put_bytes_non_atomic('test_lock/held/info', '\0')
 
349
 
352
350
        class LoggingUIFactory(bzrlib.ui.SilentUIFactory):
353
351
            def __init__(self):
354
352
                self.prompts = []
 
353
 
355
354
            def get_boolean(self, prompt):
356
355
                self.prompts.append(('boolean', prompt))
357
356
                return True
 
357
 
358
358
        ui = LoggingUIFactory()
359
359
        self.overrideAttr(bzrlib.ui, 'ui_factory', ui)
360
360
        ld2.break_lock()
372
372
        ld.create()
373
373
        ld.lock_write()
374
374
        ld.transport.delete('test_lock/held/info')
 
375
 
375
376
        class LoggingUIFactory(bzrlib.ui.SilentUIFactory):
376
377
            def __init__(self):
377
378
                self.prompts = []
 
379
 
378
380
            def get_boolean(self, prompt):
379
381
                self.prompts.append(('boolean', prompt))
380
382
                return True
 
383
 
381
384
        ui = LoggingUIFactory()
382
385
        orig_factory = bzrlib.ui.ui_factory
383
386
        bzrlib.ui.ui_factory = ui
416
419
        lf1.unlock()
417
420
        self.assertFalse(t.has('test_lock/held/info'))
418
421
 
419
 
    def test__format_lock_info(self):
 
422
    def test_display_form(self):
420
423
        ld1 = self.get_lock()
421
424
        ld1.create()
422
425
        ld1.lock_write()
423
426
        try:
424
 
            info_list = ld1._format_lock_info(ld1.peek())
 
427
            info_list = ld1.peek().to_readable_dict()
425
428
        finally:
426
429
            ld1.unlock()
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
 
430
        self.assertEqual(info_list['user'], u'jrandom@example.com')
 
431
        self.assertContainsRe(info_list['pid'], '^\d+$')
 
432
        self.assertContainsRe(info_list['time_ago'], r'^\d+ seconds? ago$')
431
433
 
432
434
    def test_lock_without_email(self):
433
435
        global_config = config.GlobalConfig()
461
463
    def test_lock_with_buggy_rename(self):
462
464
        # test that lock acquisition handles servers which pretend they
463
465
        # renamed correctly but that actually fail
464
 
        t = transport.get_transport('brokenrename+' + self.get_url())
 
466
        t = transport.get_transport_from_url(
 
467
            'brokenrename+' + self.get_url())
465
468
        ld1 = LockDir(t, 'test_lock')
466
469
        ld1.create()
467
470
        ld1.attempt_lock()
481
484
        # should be nothing before we start
482
485
        ld1.create()
483
486
        t = self.get_transport().clone('test_lock')
 
487
 
484
488
        def check_dir(a):
485
489
            self.assertEquals(a, t.list_dir('.'))
 
490
 
486
491
        check_dir([])
487
492
        # when held, that's all we see
488
493
        ld1.attempt_lock()
505
510
        t.put_bytes('test_lock/held/info', '')
506
511
        lf = LockDir(t, 'test_lock')
507
512
        info = lf.peek()
508
 
        formatted_info = lf._format_lock_info(info)
 
513
        formatted_info = info.to_readable_dict()
509
514
        self.assertEquals(
510
 
            ['<unknown>', '<unknown>', '<unknown>', '(unknown)'],
 
515
            dict(user='<unknown>', hostname='<unknown>', pid='<unknown>',
 
516
                time_ago='(unknown)'),
511
517
            formatted_info)
512
518
 
513
519
    def test_corrupt_lockdir_info(self):
646
652
        ld2.force_break(holder_info)
647
653
        lock_path = ld.transport.abspath(ld.path)
648
654
        self.assertEqual([], self._calls)
 
655
 
 
656
 
 
657
class TestLockHeldInfo(TestCase):
 
658
    """Can get information about the lock holder, and detect whether they're
 
659
    still alive."""
 
660
 
 
661
    def test_repr(self):
 
662
        info = LockHeldInfo.for_this_process(None)
 
663
        self.assertContainsRe(repr(info), r"LockHeldInfo\(.*\)")
 
664
 
 
665
    def test_unicode(self):
 
666
        info = LockHeldInfo.for_this_process(None)
 
667
        self.assertContainsRe(unicode(info),
 
668
            r'held by .* on .* \(process #\d+\), acquired .* ago')
 
669
 
 
670
    def test_is_locked_by_this_process(self):
 
671
        info = LockHeldInfo.for_this_process(None)
 
672
        self.assertTrue(info.is_locked_by_this_process())
 
673
 
 
674
    def test_is_not_locked_by_this_process(self):
 
675
        info = LockHeldInfo.for_this_process(None)
 
676
        info.info_dict['pid'] = '123123123123123'
 
677
        self.assertFalse(info.is_locked_by_this_process())
 
678
 
 
679
    def test_lock_holder_live_process(self):
 
680
        """Detect that the holder (this process) is still running."""
 
681
        info = LockHeldInfo.for_this_process(None)
 
682
        self.assertFalse(info.is_lock_holder_known_dead())
 
683
 
 
684
    def test_lock_holder_dead_process(self):
 
685
        """Detect that the holder (this process) is still running."""
 
686
        self.overrideAttr(lockdir, 'get_host_name',
 
687
            lambda: 'aproperhostname')
 
688
        info = LockHeldInfo.for_this_process(None)
 
689
        info.info_dict['pid'] = '123123123'
 
690
        self.assertTrue(info.is_lock_holder_known_dead())
 
691
 
 
692
    def test_lock_holder_other_machine(self):
 
693
        """The lock holder isn't here so we don't know if they're alive."""
 
694
        info = LockHeldInfo.for_this_process(None)
 
695
        info.info_dict['hostname'] = 'egg.example.com'
 
696
        info.info_dict['pid'] = '123123123'
 
697
        self.assertFalse(info.is_lock_holder_known_dead())
 
698
 
 
699
    def test_lock_holder_other_user(self):
 
700
        """Only auto-break locks held by this user."""
 
701
        info = LockHeldInfo.for_this_process(None)
 
702
        info.info_dict['user'] = 'notme@example.com'
 
703
        info.info_dict['pid'] = '123123123'
 
704
        self.assertFalse(info.is_lock_holder_known_dead())
 
705
 
 
706
    def test_no_good_hostname(self):
 
707
        """Correctly handle ambiguous hostnames.
 
708
 
 
709
        If the lock's recorded with just 'localhost' we can't really trust
 
710
        it's the same 'localhost'.  (There are quite a few of them. :-)
 
711
        So even if the process is known not to be alive, we can't say that's
 
712
        known for sure.
 
713
        """
 
714
        self.overrideAttr(lockdir, 'get_host_name',
 
715
            lambda: 'localhost')
 
716
        info = LockHeldInfo.for_this_process(None)
 
717
        info.info_dict['pid'] = '123123123'
 
718
        self.assertFalse(info.is_lock_holder_known_dead())
 
719
 
 
720
 
 
721
class TestStaleLockDir(TestCaseWithTransport):
 
722
    """Can automatically break stale locks.
 
723
 
 
724
    :see: https://bugs.launchpad.net/bzr/+bug/220464
 
725
    """
 
726
 
 
727
    def test_auto_break_stale_lock(self):
 
728
        """Locks safely known to be stale are just cleaned up.
 
729
 
 
730
        This generates a warning but no other user interaction.
 
731
        """
 
732
        self.overrideAttr(lockdir, 'get_host_name',
 
733
            lambda: 'aproperhostname')
 
734
        # This is off by default at present; see the discussion in the bug.
 
735
        # If you change the default, don't forget to update the docs.
 
736
        config.GlobalConfig().set_user_option('locks.steal_dead', True)
 
737
        # Create a lock pretending to come from a different nonexistent
 
738
        # process on the same machine.
 
739
        l1 = LockDir(self.get_transport(), 'a',
 
740
            extra_holder_info={'pid': '12312313'})
 
741
        token_1 = l1.attempt_lock()
 
742
        l2 = LockDir(self.get_transport(), 'a')
 
743
        token_2 = l2.attempt_lock()
 
744
        # l1 will notice its lock was stolen.
 
745
        self.assertRaises(errors.LockBroken,
 
746
            l1.unlock)
 
747
        l2.unlock()
 
748
 
 
749
    def test_auto_break_stale_lock_configured_off(self):
 
750
        """Automatic breaking can be turned off"""
 
751
        l1 = LockDir(self.get_transport(), 'a',
 
752
            extra_holder_info={'pid': '12312313'})
 
753
        token_1 = l1.attempt_lock()
 
754
        self.addCleanup(l1.unlock)
 
755
        l2 = LockDir(self.get_transport(), 'a')
 
756
        # This fails now, because dead lock breaking is off by default.
 
757
        self.assertRaises(LockContention,
 
758
            l2.attempt_lock)
 
759
        # and it's in fact not broken
 
760
        l1.confirm()