~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_lockdir.py

Move all features to bzrlib.tests.features in 2.5

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
20
21
import time
21
22
 
22
23
import bzrlib
24
25
    config,
25
26
    errors,
26
27
    lock,
 
28
    lockdir,
27
29
    osutils,
28
30
    tests,
29
31
    transport,
35
37
    LockFailed,
36
38
    LockNotHeld,
37
39
    )
38
 
from bzrlib.lockdir import LockDir
 
40
from bzrlib.lockdir import (
 
41
    LockDir,
 
42
    LockHeldInfo,
 
43
    )
39
44
from bzrlib.tests import (
40
45
    features,
 
46
    TestCase,
41
47
    TestCaseWithTransport,
42
48
    )
43
49
from bzrlib.trace import note
48
54
# implementation are tested separately.  (The main requirement is just that
49
55
# they don't allow overwriting nonempty directories.)
50
56
 
 
57
 
51
58
class TestLockDir(TestCaseWithTransport):
52
59
    """Test LockDir operations"""
53
60
 
141
148
        self.addCleanup(lf1.unlock)
142
149
        # lock is held, should get some info on it
143
150
        info1 = lf1.peek()
144
 
        self.assertEqual(set(info1.keys()),
145
 
                         set(['user', 'nonce', 'hostname', 'pid', 'start_time']))
 
151
        self.assertEqual(set(info1.info_dict.keys()),
 
152
            set(['user', 'nonce', 'hostname', 'pid', 'start_time']))
146
153
        # should get the same info if we look at it through a different
147
154
        # instance
148
155
        info2 = LockDir(t, 'test_lock').peek()
161
168
        self.addCleanup(lf1.unlock)
162
169
        info2 = lf2.peek()
163
170
        self.assertTrue(info2)
164
 
        self.assertEqual(info2['nonce'], lf1.nonce)
 
171
        self.assertEqual(info2.get('nonce'), lf1.nonce)
165
172
 
166
173
    def test_30_lock_wait_fail(self):
167
174
        """Wait on a lock, then fail
184
191
            # it should only take about 0.4 seconds, but we allow more time in
185
192
            # case the machine is heavily loaded
186
193
            self.assertTrue(after - before <= 8.0,
187
 
                    "took %f seconds to detect lock contention" % (after - before))
 
194
                "took %f seconds to detect lock contention" % (after - before))
188
195
        finally:
189
196
            lf1.unlock()
190
197
        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}')
 
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.')
205
204
 
206
205
    def test_31_lock_wait_easy(self):
207
206
        """Succeed when waiting on a lock with no contention.
349
348
        ld.create()
350
349
        ld.lock_write()
351
350
        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
 
355
356
            def get_boolean(self, prompt):
356
357
                self.prompts.append(('boolean', prompt))
357
358
                return True
 
359
 
358
360
        ui = LoggingUIFactory()
359
361
        self.overrideAttr(bzrlib.ui, 'ui_factory', ui)
360
362
        ld2.break_lock()
372
374
        ld.create()
373
375
        ld.lock_write()
374
376
        ld.transport.delete('test_lock/held/info')
 
377
 
375
378
        class LoggingUIFactory(bzrlib.ui.SilentUIFactory):
376
379
            def __init__(self):
377
380
                self.prompts = []
 
381
 
378
382
            def get_boolean(self, prompt):
379
383
                self.prompts.append(('boolean', prompt))
380
384
                return True
 
385
 
381
386
        ui = LoggingUIFactory()
382
387
        orig_factory = bzrlib.ui.ui_factory
383
388
        bzrlib.ui.ui_factory = ui
416
421
        lf1.unlock()
417
422
        self.assertFalse(t.has('test_lock/held/info'))
418
423
 
419
 
    def test__format_lock_info(self):
 
424
    def test_display_form(self):
420
425
        ld1 = self.get_lock()
421
426
        ld1.create()
422
427
        ld1.lock_write()
423
428
        try:
424
 
            info_list = ld1._format_lock_info(ld1.peek())
 
429
            info_list = ld1.peek().to_readable_dict()
425
430
        finally:
426
431
            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
 
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$')
431
435
 
432
436
    def test_lock_without_email(self):
433
437
        global_config = config.GlobalConfig()
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(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()