~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_lockdir.py

  • Committer: Richard Wilbur
  • Date: 2016-02-04 19:07:28 UTC
  • mto: This revision was merged to the branch mainline in revision 6618.
  • Revision ID: richard.wilbur@gmail.com-20160204190728-p0zvfii6zase0fw7
Update COPYING.txt from the original http://www.gnu.org/licenses/gpl-2.0.txt  (Only differences were in whitespace.)  Thanks to Petr Stodulka for pointing out the discrepancy.

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,
 
46
    TestCaseInTempDir,
41
47
    TestCaseWithTransport,
42
48
    )
43
 
from bzrlib.trace import note
44
49
 
45
50
# These tests are run on the default transport provided by the test framework
46
51
# (typically a local disk transport).  That can be changed by the --transport
48
53
# implementation are tested separately.  (The main requirement is just that
49
54
# they don't allow overwriting nonempty directories.)
50
55
 
 
56
 
51
57
class TestLockDir(TestCaseWithTransport):
52
58
    """Test LockDir operations"""
53
59
 
141
147
        self.addCleanup(lf1.unlock)
142
148
        # lock is held, should get some info on it
143
149
        info1 = lf1.peek()
144
 
        self.assertEqual(set(info1.keys()),
145
 
                         set(['user', 'nonce', 'hostname', 'pid', 'start_time']))
 
150
        self.assertEqual(set(info1.info_dict.keys()),
 
151
            set(['user', 'nonce', 'hostname', 'pid', 'start_time']))
146
152
        # should get the same info if we look at it through a different
147
153
        # instance
148
154
        info2 = LockDir(t, 'test_lock').peek()
161
167
        self.addCleanup(lf1.unlock)
162
168
        info2 = lf2.peek()
163
169
        self.assertTrue(info2)
164
 
        self.assertEqual(info2['nonce'], lf1.nonce)
 
170
        self.assertEqual(info2.get('nonce'), lf1.nonce)
165
171
 
166
172
    def test_30_lock_wait_fail(self):
167
173
        """Wait on a lock, then fail
184
190
            # it should only take about 0.4 seconds, but we allow more time in
185
191
            # case the machine is heavily loaded
186
192
            self.assertTrue(after - before <= 8.0,
187
 
                    "took %f seconds to detect lock contention" % (after - before))
 
193
                "took %f seconds to detect lock contention" % (after - before))
188
194
        finally:
189
195
            lf1.unlock()
190
196
        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}')
 
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.')
205
203
 
206
204
    def test_31_lock_wait_easy(self):
207
205
        """Succeed when waiting on a lock with no contention.
349
347
        ld.create()
350
348
        ld.lock_write()
351
349
        ld.transport.put_bytes_non_atomic('test_lock/held/info', '\0')
 
350
 
352
351
        class LoggingUIFactory(bzrlib.ui.SilentUIFactory):
353
352
            def __init__(self):
354
353
                self.prompts = []
 
354
 
355
355
            def get_boolean(self, prompt):
356
356
                self.prompts.append(('boolean', prompt))
357
357
                return True
 
358
 
358
359
        ui = LoggingUIFactory()
359
360
        self.overrideAttr(bzrlib.ui, 'ui_factory', ui)
360
361
        ld2.break_lock()
372
373
        ld.create()
373
374
        ld.lock_write()
374
375
        ld.transport.delete('test_lock/held/info')
 
376
 
375
377
        class LoggingUIFactory(bzrlib.ui.SilentUIFactory):
376
378
            def __init__(self):
377
379
                self.prompts = []
 
380
 
378
381
            def get_boolean(self, prompt):
379
382
                self.prompts.append(('boolean', prompt))
380
383
                return True
 
384
 
381
385
        ui = LoggingUIFactory()
382
386
        orig_factory = bzrlib.ui.ui_factory
383
387
        bzrlib.ui.ui_factory = ui
416
420
        lf1.unlock()
417
421
        self.assertFalse(t.has('test_lock/held/info'))
418
422
 
419
 
    def test__format_lock_info(self):
 
423
    def test_display_form(self):
420
424
        ld1 = self.get_lock()
421
425
        ld1.create()
422
426
        ld1.lock_write()
423
427
        try:
424
 
            info_list = ld1._format_lock_info(ld1.peek())
 
428
            info_list = ld1.peek().to_readable_dict()
425
429
        finally:
426
430
            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
 
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$')
431
434
 
432
435
    def test_lock_without_email(self):
433
 
        global_config = config.GlobalConfig()
 
436
        global_config = config.GlobalStack()
434
437
        # Intentionally has no email address
435
 
        global_config.set_user_option('email', 'User Identity')
 
438
        global_config.set('email', 'User Identity')
436
439
        ld1 = self.get_lock()
437
440
        ld1.create()
438
441
        ld1.lock_write()
461
464
    def test_lock_with_buggy_rename(self):
462
465
        # test that lock acquisition handles servers which pretend they
463
466
        # renamed correctly but that actually fail
464
 
        t = transport.get_transport('brokenrename+' + self.get_url())
 
467
        t = transport.get_transport_from_url(
 
468
            'brokenrename+' + self.get_url())
465
469
        ld1 = LockDir(t, 'test_lock')
466
470
        ld1.create()
467
471
        ld1.attempt_lock()
481
485
        # should be nothing before we start
482
486
        ld1.create()
483
487
        t = self.get_transport().clone('test_lock')
 
488
 
484
489
        def check_dir(a):
485
490
            self.assertEquals(a, t.list_dir('.'))
 
491
 
486
492
        check_dir([])
487
493
        # when held, that's all we see
488
494
        ld1.attempt_lock()
505
511
        t.put_bytes('test_lock/held/info', '')
506
512
        lf = LockDir(t, 'test_lock')
507
513
        info = lf.peek()
508
 
        formatted_info = lf._format_lock_info(info)
 
514
        formatted_info = info.to_readable_dict()
509
515
        self.assertEquals(
510
 
            ['<unknown>', '<unknown>', '<unknown>', '(unknown)'],
 
516
            dict(user='<unknown>', hostname='<unknown>', pid='<unknown>',
 
517
                time_ago='(unknown)'),
511
518
            formatted_info)
512
519
 
513
520
    def test_corrupt_lockdir_info(self):
646
653
        ld2.force_break(holder_info)
647
654
        lock_path = ld.transport.abspath(ld.path)
648
655
        self.assertEqual([], self._calls)
 
656
 
 
657
 
 
658
class TestLockHeldInfo(TestCaseInTempDir):
 
659
    """Can get information about the lock holder, and detect whether they're
 
660
    still alive."""
 
661
 
 
662
    def test_repr(self):
 
663
        info = LockHeldInfo.for_this_process(None)
 
664
        self.assertContainsRe(repr(info), r"LockHeldInfo\(.*\)")
 
665
 
 
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')
 
670
 
 
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())
 
674
 
 
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())
 
679
 
 
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())
 
684
 
 
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())
 
692
 
 
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())
 
699
 
 
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())
 
706
 
 
707
    def test_no_good_hostname(self):
 
708
        """Correctly handle ambiguous hostnames.
 
709
 
 
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
 
713
        known for sure.
 
714
        """
 
715
        self.overrideAttr(lockdir, 'get_host_name',
 
716
            lambda: 'localhost')
 
717
        info = LockHeldInfo.for_this_process(None)
 
718
        info.info_dict['pid'] = '123123123'
 
719
        self.assertFalse(info.is_lock_holder_known_dead())
 
720
 
 
721
 
 
722
class TestStaleLockDir(TestCaseWithTransport):
 
723
    """Can automatically break stale locks.
 
724
 
 
725
    :see: https://bugs.launchpad.net/bzr/+bug/220464
 
726
    """
 
727
 
 
728
    def test_auto_break_stale_lock(self):
 
729
        """Locks safely known to be stale are just cleaned up.
 
730
 
 
731
        This generates a warning but no other user interaction.
 
732
        """
 
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,
 
747
            l1.unlock)
 
748
        l2.unlock()
 
749
 
 
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,
 
759
            l2.attempt_lock)
 
760
        # and it's in fact not broken
 
761
        l1.confirm()