~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_lockdir.py

  • Committer: Jonathan Riddell
  • Date: 2011-05-16 11:27:37 UTC
  • mto: This revision was merged to the branch mainline in revision 5869.
  • Revision ID: jriddell@canonical.com-20110516112737-gep642p24rtzp3jt
userĀ guideĀ licence

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2012, 2016 Canonical Ltd
 
1
# Copyright (C) 2006-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
24
24
    config,
25
25
    errors,
26
26
    lock,
27
 
    lockdir,
28
27
    osutils,
29
28
    tests,
30
29
    transport,
36
35
    LockFailed,
37
36
    LockNotHeld,
38
37
    )
39
 
from bzrlib.lockdir import (
40
 
    LockDir,
41
 
    LockHeldInfo,
42
 
    )
 
38
from bzrlib.lockdir import LockDir
43
39
from bzrlib.tests import (
44
40
    features,
45
 
    TestCase,
46
 
    TestCaseInTempDir,
47
41
    TestCaseWithTransport,
48
42
    )
 
43
from bzrlib.trace import note
49
44
 
50
45
# These tests are run on the default transport provided by the test framework
51
46
# (typically a local disk transport).  That can be changed by the --transport
53
48
# implementation are tested separately.  (The main requirement is just that
54
49
# they don't allow overwriting nonempty directories.)
55
50
 
56
 
 
57
51
class TestLockDir(TestCaseWithTransport):
58
52
    """Test LockDir operations"""
59
53
 
147
141
        self.addCleanup(lf1.unlock)
148
142
        # lock is held, should get some info on it
149
143
        info1 = lf1.peek()
150
 
        self.assertEqual(set(info1.info_dict.keys()),
151
 
            set(['user', 'nonce', 'hostname', 'pid', 'start_time']))
 
144
        self.assertEqual(set(info1.keys()),
 
145
                         set(['user', 'nonce', 'hostname', 'pid', 'start_time']))
152
146
        # should get the same info if we look at it through a different
153
147
        # instance
154
148
        info2 = LockDir(t, 'test_lock').peek()
167
161
        self.addCleanup(lf1.unlock)
168
162
        info2 = lf2.peek()
169
163
        self.assertTrue(info2)
170
 
        self.assertEqual(info2.get('nonce'), lf1.nonce)
 
164
        self.assertEqual(info2['nonce'], lf1.nonce)
171
165
 
172
166
    def test_30_lock_wait_fail(self):
173
167
        """Wait on a lock, then fail
190
184
            # it should only take about 0.4 seconds, but we allow more time in
191
185
            # case the machine is heavily loaded
192
186
            self.assertTrue(after - before <= 8.0,
193
 
                "took %f seconds to detect lock contention" % (after - before))
 
187
                    "took %f seconds to detect lock contention" % (after - before))
194
188
        finally:
195
189
            lf1.unlock()
196
190
        self.assertEqual(1, len(self._logged_reports))
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.')
 
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}')
203
205
 
204
206
    def test_31_lock_wait_easy(self):
205
207
        """Succeed when waiting on a lock with no contention.
347
349
        ld.create()
348
350
        ld.lock_write()
349
351
        ld.transport.put_bytes_non_atomic('test_lock/held/info', '\0')
350
 
 
351
352
        class LoggingUIFactory(bzrlib.ui.SilentUIFactory):
352
353
            def __init__(self):
353
354
                self.prompts = []
354
 
 
355
355
            def get_boolean(self, prompt):
356
356
                self.prompts.append(('boolean', prompt))
357
357
                return True
358
 
 
359
358
        ui = LoggingUIFactory()
360
359
        self.overrideAttr(bzrlib.ui, 'ui_factory', ui)
361
360
        ld2.break_lock()
373
372
        ld.create()
374
373
        ld.lock_write()
375
374
        ld.transport.delete('test_lock/held/info')
376
 
 
377
375
        class LoggingUIFactory(bzrlib.ui.SilentUIFactory):
378
376
            def __init__(self):
379
377
                self.prompts = []
380
 
 
381
378
            def get_boolean(self, prompt):
382
379
                self.prompts.append(('boolean', prompt))
383
380
                return True
384
 
 
385
381
        ui = LoggingUIFactory()
386
382
        orig_factory = bzrlib.ui.ui_factory
387
383
        bzrlib.ui.ui_factory = ui
420
416
        lf1.unlock()
421
417
        self.assertFalse(t.has('test_lock/held/info'))
422
418
 
423
 
    def test_display_form(self):
 
419
    def test__format_lock_info(self):
424
420
        ld1 = self.get_lock()
425
421
        ld1.create()
426
422
        ld1.lock_write()
427
423
        try:
428
 
            info_list = ld1.peek().to_readable_dict()
 
424
            info_list = ld1._format_lock_info(ld1.peek())
429
425
        finally:
430
426
            ld1.unlock()
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$')
 
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
434
431
 
435
432
    def test_lock_without_email(self):
436
 
        global_config = config.GlobalStack()
 
433
        global_config = config.GlobalConfig()
437
434
        # Intentionally has no email address
438
 
        global_config.set('email', 'User Identity')
 
435
        global_config.set_user_option('email', 'User Identity')
439
436
        ld1 = self.get_lock()
440
437
        ld1.create()
441
438
        ld1.lock_write()
464
461
    def test_lock_with_buggy_rename(self):
465
462
        # test that lock acquisition handles servers which pretend they
466
463
        # renamed correctly but that actually fail
467
 
        t = transport.get_transport_from_url(
468
 
            'brokenrename+' + self.get_url())
 
464
        t = transport.get_transport('brokenrename+' + self.get_url())
469
465
        ld1 = LockDir(t, 'test_lock')
470
466
        ld1.create()
471
467
        ld1.attempt_lock()
475
471
        # now the original caller should succeed in unlocking
476
472
        ld1.unlock()
477
473
        # and there should be nothing left over
478
 
        self.assertEqual([], t.list_dir('test_lock'))
 
474
        self.assertEquals([], t.list_dir('test_lock'))
479
475
 
480
476
    def test_failed_lock_leaves_no_trash(self):
481
477
        # if we fail to acquire the lock, we don't leave pending directories
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
 
            self.assertEqual(a, t.list_dir('.'))
491
 
 
 
485
            self.assertEquals(a, t.list_dir('.'))
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()
515
 
        self.assertEqual(
516
 
            dict(user='<unknown>', hostname='<unknown>', pid='<unknown>',
517
 
                time_ago='(unknown)'),
 
508
        formatted_info = lf._format_lock_info(info)
 
509
        self.assertEquals(
 
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(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()