~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_lockdir.py

(jameinel) (bug #780544) when updating the WT,
 allow it to be done with a fast delta,
 rather than setting the state from scratch. (John A Meinel)

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
"""Tests for LockDir"""
18
18
 
19
19
import os
20
 
import sys
21
20
import time
22
21
 
23
22
import bzrlib
25
24
    config,
26
25
    errors,
27
26
    lock,
28
 
    lockdir,
29
27
    osutils,
30
28
    tests,
31
29
    transport,
37
35
    LockFailed,
38
36
    LockNotHeld,
39
37
    )
40
 
from bzrlib.lockdir import (
41
 
    LockDir,
42
 
    LockHeldInfo,
43
 
    )
 
38
from bzrlib.lockdir import LockDir
44
39
from bzrlib.tests import (
45
40
    features,
46
 
    TestCase,
47
41
    TestCaseWithTransport,
48
42
    )
49
43
from bzrlib.trace import note
54
48
# implementation are tested separately.  (The main requirement is just that
55
49
# they don't allow overwriting nonempty directories.)
56
50
 
57
 
 
58
51
class TestLockDir(TestCaseWithTransport):
59
52
    """Test LockDir operations"""
60
53
 
148
141
        self.addCleanup(lf1.unlock)
149
142
        # lock is held, should get some info on it
150
143
        info1 = lf1.peek()
151
 
        self.assertEqual(set(info1.info_dict.keys()),
152
 
            set(['user', 'nonce', 'hostname', 'pid', 'start_time']))
 
144
        self.assertEqual(set(info1.keys()),
 
145
                         set(['user', 'nonce', 'hostname', 'pid', 'start_time']))
153
146
        # should get the same info if we look at it through a different
154
147
        # instance
155
148
        info2 = LockDir(t, 'test_lock').peek()
168
161
        self.addCleanup(lf1.unlock)
169
162
        info2 = lf2.peek()
170
163
        self.assertTrue(info2)
171
 
        self.assertEqual(info2.get('nonce'), lf1.nonce)
 
164
        self.assertEqual(info2['nonce'], lf1.nonce)
172
165
 
173
166
    def test_30_lock_wait_fail(self):
174
167
        """Wait on a lock, then fail
191
184
            # it should only take about 0.4 seconds, but we allow more time in
192
185
            # case the machine is heavily loaded
193
186
            self.assertTrue(after - before <= 8.0,
194
 
                "took %f seconds to detect lock contention" % (after - before))
 
187
                    "took %f seconds to detect lock contention" % (after - before))
195
188
        finally:
196
189
            lf1.unlock()
197
190
        self.assertEqual(1, len(self._logged_reports))
198
 
        self.assertContainsRe(self._logged_reports[0][0],
199
 
            r'Unable to obtain lock .* held by jrandom@example\.com on .*'
200
 
            r' \(process #\d+\), acquired .* ago\.\n'
201
 
            r'Will continue to try until \d{2}:\d{2}:\d{2}, unless '
202
 
            r'you press Ctrl-C.\n'
203
 
            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')
 
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}')
204
205
 
205
206
    def test_31_lock_wait_easy(self):
206
207
        """Succeed when waiting on a lock with no contention.
348
349
        ld.create()
349
350
        ld.lock_write()
350
351
        ld.transport.put_bytes_non_atomic('test_lock/held/info', '\0')
351
 
 
352
352
        class LoggingUIFactory(bzrlib.ui.SilentUIFactory):
353
353
            def __init__(self):
354
354
                self.prompts = []
355
 
 
356
355
            def get_boolean(self, prompt):
357
356
                self.prompts.append(('boolean', prompt))
358
357
                return True
359
 
 
360
358
        ui = LoggingUIFactory()
361
359
        self.overrideAttr(bzrlib.ui, 'ui_factory', ui)
362
360
        ld2.break_lock()
374
372
        ld.create()
375
373
        ld.lock_write()
376
374
        ld.transport.delete('test_lock/held/info')
377
 
 
378
375
        class LoggingUIFactory(bzrlib.ui.SilentUIFactory):
379
376
            def __init__(self):
380
377
                self.prompts = []
381
 
 
382
378
            def get_boolean(self, prompt):
383
379
                self.prompts.append(('boolean', prompt))
384
380
                return True
385
 
 
386
381
        ui = LoggingUIFactory()
387
382
        orig_factory = bzrlib.ui.ui_factory
388
383
        bzrlib.ui.ui_factory = ui
421
416
        lf1.unlock()
422
417
        self.assertFalse(t.has('test_lock/held/info'))
423
418
 
424
 
    def test_display_form(self):
 
419
    def test__format_lock_info(self):
425
420
        ld1 = self.get_lock()
426
421
        ld1.create()
427
422
        ld1.lock_write()
428
423
        try:
429
 
            info_list = ld1.peek().to_readable_dict()
 
424
            info_list = ld1._format_lock_info(ld1.peek())
430
425
        finally:
431
426
            ld1.unlock()
432
 
        self.assertEqual(info_list['user'], u'jrandom@example.com')
433
 
        self.assertContainsRe(info_list['pid'], '^\d+$')
434
 
        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
431
 
436
432
    def test_lock_without_email(self):
437
433
        global_config = config.GlobalConfig()
485
481
        # should be nothing before we start
486
482
        ld1.create()
487
483
        t = self.get_transport().clone('test_lock')
488
 
 
489
484
        def check_dir(a):
490
485
            self.assertEquals(a, t.list_dir('.'))
491
 
 
492
486
        check_dir([])
493
487
        # when held, that's all we see
494
488
        ld1.attempt_lock()
511
505
        t.put_bytes('test_lock/held/info', '')
512
506
        lf = LockDir(t, 'test_lock')
513
507
        info = lf.peek()
514
 
        formatted_info = info.to_readable_dict()
 
508
        formatted_info = lf._format_lock_info(info)
515
509
        self.assertEquals(
516
 
            dict(user='<unknown>', hostname='<unknown>', pid='<unknown>',
517
 
                time_ago='(unknown)'),
 
510
            ['<unknown>', '<unknown>', '<unknown>', '(unknown)'],
518
511
            formatted_info)
519
512
 
520
513
    def test_corrupt_lockdir_info(self):
653
646
        ld2.force_break(holder_info)
654
647
        lock_path = ld.transport.abspath(ld.path)
655
648
        self.assertEqual([], self._calls)
656
 
 
657
 
 
658
 
class TestLockHeldInfo(TestCase):
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
 
        info = LockHeldInfo.for_this_process(None)
688
 
        info.info_dict['pid'] = '123123123'
689
 
        if sys.platform == 'win32':
690
 
            self.knownFailure(
691
 
                'live lock holder detection not implemented yet on win32')
692
 
        self.assertTrue(info.is_lock_holder_known_dead())
693
 
 
694
 
    def test_lock_holder_other_machine(self):
695
 
        """The lock holder isn't here so we don't know if they're alive."""
696
 
        info = LockHeldInfo.for_this_process(None)
697
 
        info.info_dict['hostname'] = 'egg.example.com'
698
 
        info.info_dict['pid'] = '123123123'
699
 
        self.assertFalse(info.is_lock_holder_known_dead())
700
 
 
701
 
    def test_lock_holder_other_user(self):
702
 
        """Only auto-break locks held by this user."""
703
 
        info = LockHeldInfo.for_this_process(None)
704
 
        info.info_dict['user'] = 'notme@example.com'
705
 
        info.info_dict['pid'] = '123123123'
706
 
        self.assertFalse(info.is_lock_holder_known_dead())
707
 
 
708
 
    def test_no_good_hostname(self):
709
 
        """Correctly handle ambiguous hostnames.
710
 
 
711
 
        If the lock's recorded with just 'localhost' we can't really trust
712
 
        it's the same 'localhost'.  (There are quite a few of them. :-)
713
 
        So even if the process is known not to be alive, we can't say that's
714
 
        known for sure.
715
 
        """
716
 
        self.overrideAttr(lockdir, 'get_host_name',
717
 
            lambda: 'localhost')
718
 
        info = LockHeldInfo.for_this_process(None)
719
 
        info.info_dict['pid'] = '123123123'
720
 
        self.assertFalse(info.is_lock_holder_known_dead())
721
 
 
722
 
 
723
 
class TestStaleLockDir(TestCaseWithTransport):
724
 
    """Can automatically break stale locks.
725
 
 
726
 
    :see: https://bugs.launchpad.net/bzr/+bug/220464
727
 
    """
728
 
 
729
 
    def test_auto_break_stale_lock(self):
730
 
        """Locks safely known to be stale are just cleaned up.
731
 
 
732
 
        This generates a warning but no other user interaction.
733
 
        """
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()