/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_lockdir.py

Add bzrlib.pyutils, which has get_named_object, a wrapper around __import__.

This is used to replace various ad hoc implementations of the same logic,
notably the version used in registry's _LazyObjectGetter which had a bug when
getting a module without also getting a member.  And of course, this new
function has unit tests, unlike the replaced code.

This also adds a KnownHooksRegistry subclass to provide a more natural home for
some other logic.

I'm not thrilled about the name of the new module or the new functions, but it's
hard to think of good names for such generic functionality.

Show diffs side-by-side

added added

removed removed

Lines of Context:
191
191
                    "took %f seconds to detect lock contention" % (after - before))
192
192
        finally:
193
193
            lf1.unlock()
194
 
        lock_base = lf2.transport.abspath(lf2.path)
195
194
        self.assertEqual(1, len(self._logged_reports))
196
 
        lock_url = lf2.transport.abspath(lf2.path)
197
 
        self.assertEqual('%s %s\n'
198
 
                         '%s\n%s\n'
199
 
                         'Will continue to try until %s, unless '
200
 
                         'you press Ctrl-C.\n'
201
 
                         'See "bzr help break-lock" for more.',
202
 
                         self._logged_reports[0][0])
203
 
        args = self._logged_reports[0][1]
204
 
        self.assertEqual('Unable to obtain', args[0])
205
 
        self.assertEqual('lock %s' % (lock_base,), args[1])
206
 
        self.assertStartsWith(args[2], 'held by ')
207
 
        self.assertStartsWith(args[3], 'locked ')
208
 
        self.assertEndsWith(args[3], ' ago')
209
 
        self.assertContainsRe(args[4], r'\d\d:\d\d:\d\d')
 
195
        self.assertEqual(self._logged_reports[0][0],
 
196
            '%s lock %s held by %s\n'
 
197
            'at %s [process #%s], acquired %s.\n'
 
198
            'Will continue to try until %s, unless '
 
199
            'you press Ctrl-C.\n'
 
200
            'See "bzr help break-lock" for more.')
 
201
        start, lock_url, user, hostname, pid, time_ago, deadline_str = \
 
202
            self._logged_reports[0][1]
 
203
        self.assertEqual(start, u'Unable to obtain')
 
204
        self.assertEqual(user, u'jrandom@example.com')
 
205
        # skip hostname
 
206
        self.assertContainsRe(pid, r'\d+')
 
207
        self.assertContainsRe(time_ago, r'.* ago')
 
208
        self.assertContainsRe(deadline_str, r'\d{2}:\d{2}:\d{2}')
210
209
 
211
210
    def test_31_lock_wait_easy(self):
212
211
        """Succeed when waiting on a lock with no contention.
565
564
        finally:
566
565
            bzrlib.ui.ui_factory = orig_factory
567
566
 
 
567
    def test_break_lock_corrupt_info(self):
 
568
        """break_lock works even if the info file is corrupt (and tells the UI
 
569
        that it is corrupt).
 
570
        """
 
571
        ld = self.get_lock()
 
572
        ld2 = self.get_lock()
 
573
        ld.create()
 
574
        ld.lock_write()
 
575
        ld.transport.put_bytes_non_atomic('test_lock/held/info', '\0')
 
576
        class LoggingUIFactory(bzrlib.ui.SilentUIFactory):
 
577
            def __init__(self):
 
578
                self.prompts = []
 
579
            def get_boolean(self, prompt):
 
580
                self.prompts.append(('boolean', prompt))
 
581
                return True
 
582
        ui = LoggingUIFactory()
 
583
        orig_factory = bzrlib.ui.ui_factory
 
584
        bzrlib.ui.ui_factory = ui
 
585
        try:
 
586
            ld2.break_lock()
 
587
            self.assertLength(1, ui.prompts)
 
588
            self.assertEqual('boolean', ui.prompts[0][0])
 
589
            self.assertStartsWith(ui.prompts[0][1], 'Break (corrupt LockDir')
 
590
            self.assertRaises(LockBroken, ld.unlock)
 
591
        finally:
 
592
            bzrlib.ui.ui_factory = orig_factory
 
593
 
 
594
    def test_break_lock_missing_info(self):
 
595
        """break_lock works even if the info file is missing (and tells the UI
 
596
        that it is corrupt).
 
597
        """
 
598
        ld = self.get_lock()
 
599
        ld2 = self.get_lock()
 
600
        ld.create()
 
601
        ld.lock_write()
 
602
        ld.transport.delete('test_lock/held/info')
 
603
        class LoggingUIFactory(bzrlib.ui.SilentUIFactory):
 
604
            def __init__(self):
 
605
                self.prompts = []
 
606
            def get_boolean(self, prompt):
 
607
                self.prompts.append(('boolean', prompt))
 
608
                return True
 
609
        ui = LoggingUIFactory()
 
610
        orig_factory = bzrlib.ui.ui_factory
 
611
        bzrlib.ui.ui_factory = ui
 
612
        try:
 
613
            ld2.break_lock()
 
614
            self.assertRaises(LockBroken, ld.unlock)
 
615
            self.assertLength(0, ui.prompts)
 
616
        finally:
 
617
            bzrlib.ui.ui_factory = orig_factory
 
618
        # Suppress warnings due to ld not being unlocked
 
619
        # XXX: if lock_broken hook was invoked in this case, this hack would
 
620
        # not be necessary.  - Andrew Bennetts, 2010-09-06.
 
621
        del self._lock_actions[:]
 
622
 
568
623
    def test_create_missing_base_directory(self):
569
624
        """If LockDir.path doesn't exist, it can be created
570
625
 
597
652
            info_list = ld1._format_lock_info(ld1.peek())
598
653
        finally:
599
654
            ld1.unlock()
600
 
        self.assertEqual('lock %s' % (ld1.transport.abspath(ld1.path),),
601
 
                         info_list[0])
602
 
        self.assertContainsRe(info_list[1],
603
 
                              r'^held by .* on host .* \[process #\d*\]$')
604
 
        self.assertContainsRe(info_list[2], r'locked \d+ seconds? ago$')
 
655
        self.assertEqual(info_list[0], u'jrandom@example.com')
 
656
        # info_list[1] is hostname. we skip this.
 
657
        self.assertContainsRe(info_list[2], '^\d+$') # pid
 
658
        self.assertContainsRe(info_list[3], r'^\d+ seconds? ago$') # time_ago
605
659
 
606
660
    def test_lock_without_email(self):
607
661
        global_config = config.GlobalConfig()
669
723
    def test_no_lockdir_info(self):
670
724
        """We can cope with empty info files."""
671
725
        # This seems like a fairly common failure case - see
672
 
        # <https://bugs.edge.launchpad.net/bzr/+bug/185103> and all its dupes.
 
726
        # <https://bugs.launchpad.net/bzr/+bug/185103> and all its dupes.
673
727
        # Processes are often interrupted after opening the file
674
728
        # before the actual contents are committed.
675
729
        t = self.get_transport()
680
734
        info = lf.peek()
681
735
        formatted_info = lf._format_lock_info(info)
682
736
        self.assertEquals(
683
 
            ['lock %s' % t.abspath('test_lock'),
684
 
             'held by <unknown> on host <unknown> [process #<unknown>]',
685
 
             'locked (unknown)'],
 
737
            ['<unknown>', '<unknown>', '<unknown>', '(unknown)'],
686
738
            formatted_info)
687
739
 
 
740
    def test_corrupt_lockdir_info(self):
 
741
        """We can cope with corrupt (and thus unparseable) info files."""
 
742
        # This seems like a fairly common failure case too - see
 
743
        # <https://bugs.edge.launchpad.net/bzr/+bug/619872> for instance.
 
744
        # In particular some systems tend to fill recently created files with
 
745
        # nul bytes after recovering from a system crash.
 
746
        t = self.get_transport()
 
747
        t.mkdir('test_lock')
 
748
        t.mkdir('test_lock/held')
 
749
        t.put_bytes('test_lock/held/info', '\0')
 
750
        lf = LockDir(t, 'test_lock')
 
751
        self.assertRaises(errors.LockCorrupt, lf.peek)
 
752
        # Currently attempt_lock gives LockContention, but LockCorrupt would be
 
753
        # a reasonable result too.
 
754
        self.assertRaises(
 
755
            (errors.LockCorrupt, errors.LockContention), lf.attempt_lock)
 
756
        self.assertRaises(errors.LockCorrupt, lf.validate_token, 'fake token')
 
757
 
 
758
    def test_missing_lockdir_info(self):
 
759
        """We can cope with absent info files."""
 
760
        t = self.get_transport()
 
761
        t.mkdir('test_lock')
 
762
        t.mkdir('test_lock/held')
 
763
        lf = LockDir(t, 'test_lock')
 
764
        # In this case we expect the 'not held' result from peek, because peek
 
765
        # cannot be expected to notice that there is a 'held' directory with no
 
766
        # 'info' file.
 
767
        self.assertEqual(None, lf.peek())
 
768
        # And lock/unlock may work or give LockContention (but not any other
 
769
        # error).
 
770
        try:
 
771
            lf.attempt_lock()
 
772
        except LockContention:
 
773
            # LockContention is ok, and expected on Windows
 
774
            pass
 
775
        else:
 
776
            # no error is ok, and expected on POSIX (because POSIX allows
 
777
            # os.rename over an empty directory).
 
778
            lf.unlock()
 
779
        # Currently raises TokenMismatch, but LockCorrupt would be reasonable
 
780
        # too.
 
781
        self.assertRaises(
 
782
            (errors.TokenMismatch, errors.LockCorrupt),
 
783
            lf.validate_token, 'fake token')
 
784
 
688
785
 
689
786
class TestLockDirHooks(TestCaseWithTransport):
690
787