~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_errors.py

  • Committer: Robert Collins
  • Date: 2008-08-14 06:54:35 UTC
  • mto: This revision was merged to the branch mainline in revision 3629.
  • Revision ID: robertc@robertcollins.net-20080814065435-73xgy9r6xtqmvzux
Review feedback.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2012, 2016 Canonical Ltd
 
1
# Copyright (C) 2006, 2007, 2008 Canonical Ltd
 
2
#   Authors: Robert Collins <robert.collins@canonical.com>
 
3
#            and others
2
4
#
3
5
# This program is free software; you can redistribute it and/or modify
4
6
# it under the terms of the GNU General Public License as published by
12
14
#
13
15
# You should have received a copy of the GNU General Public License
14
16
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
18
 
17
19
"""Tests for the formatting and construction of errors."""
18
20
 
19
 
import inspect
20
 
import re
21
 
import socket
22
21
import sys
23
 
 
24
22
from bzrlib import (
25
 
    controldir,
 
23
    bzrdir,
26
24
    errors,
27
25
    osutils,
 
26
    symbol_versioning,
28
27
    urlutils,
29
28
    )
30
 
from bzrlib.tests import (
31
 
    TestCase,
32
 
    TestCaseWithTransport,
33
 
    TestSkipped,
34
 
    )
 
29
from bzrlib.tests import TestCase, TestCaseWithTransport
35
30
 
36
31
 
37
32
class TestErrors(TestCaseWithTransport):
38
33
 
39
 
    def test_no_arg_named_message(self):
40
 
        """Ensure the __init__ and _fmt in errors do not have "message" arg.
41
 
 
42
 
        This test fails if __init__ or _fmt in errors has an argument
43
 
        named "message" as this can cause errors in some Python versions.
44
 
        Python 2.5 uses a slot for StandardError.message.
45
 
        See bug #603461
46
 
        """
47
 
        fmt_pattern = re.compile("%\(message\)[sir]")
48
 
        subclasses_present = getattr(errors.BzrError, '__subclasses__', None)
49
 
        if not subclasses_present:
50
 
            raise TestSkipped('__subclasses__ attribute required for classes. '
51
 
                'Requires Python 2.5 or later.')
52
 
        for c in errors.BzrError.__subclasses__():
53
 
            init = getattr(c, '__init__', None)
54
 
            fmt = getattr(c, '_fmt', None)
55
 
            if init:
56
 
                args = inspect.getargspec(init)[0]
57
 
                self.assertFalse('message' in args,
58
 
                    ('Argument name "message" not allowed for '
59
 
                    '"errors.%s.__init__"' % c.__name__))
60
 
            if fmt and fmt_pattern.search(fmt):
61
 
                self.assertFalse(True, ('"message" not allowed in '
62
 
                    '"errors.%s._fmt"' % c.__name__))
63
 
 
64
34
    def test_bad_filename_encoding(self):
65
35
        error = errors.BadFilenameEncoding('bad/filen\xe5me', 'UTF-8')
66
36
        self.assertEqualDiff(
75
45
            "Error: the reason why",
76
46
            str(error))
77
47
 
78
 
    def test_dirstate_corrupt(self):
79
 
        error = errors.DirstateCorrupt('.bzr/checkout/dirstate',
80
 
                                       'trailing garbage: "x"')
81
 
        self.assertEqualDiff("The dirstate file (.bzr/checkout/dirstate)"
82
 
            " appears to be corrupt: trailing garbage: \"x\"",
83
 
            str(error))
84
 
 
85
48
    def test_disabled_method(self):
86
49
        error = errors.DisabledMethod("class name")
87
50
        self.assertEqualDiff(
117
80
            "reason: reason for foo",
118
81
            str(error))
119
82
 
120
 
    def test_inconsistent_delta_delta(self):
121
 
        error = errors.InconsistentDeltaDelta([], 'reason')
122
 
        self.assertEqualDiff(
123
 
            "An inconsistent delta was supplied: []\nreason: reason",
124
 
            str(error))
125
 
 
126
83
    def test_in_process_transport(self):
127
84
        error = errors.InProcessTransport('fpp')
128
85
        self.assertEqualDiff(
133
90
        error = errors.InvalidHttpRange('path',
134
91
                                        'Content-Range: potatoes 0-00/o0oo0',
135
92
                                        'bad range')
136
 
        self.assertEqual("Invalid http range"
137
 
                         " 'Content-Range: potatoes 0-00/o0oo0'"
138
 
                         " for path: bad range",
139
 
                         str(error))
 
93
        self.assertEquals("Invalid http range"
 
94
                          " 'Content-Range: potatoes 0-00/o0oo0'"
 
95
                          " for path: bad range",
 
96
                          str(error))
140
97
 
141
98
    def test_invalid_range(self):
142
99
        error = errors.InvalidRange('path', 12, 'bad range')
143
 
        self.assertEqual("Invalid range access in path at 12: bad range",
144
 
                         str(error))
 
100
        self.assertEquals("Invalid range access in path at 12: bad range",
 
101
                          str(error))
145
102
 
146
103
    def test_inventory_modified(self):
147
104
        error = errors.InventoryModified("a tree to be repred")
150
107
            "read without data loss.",
151
108
            str(error))
152
109
 
153
 
    def test_jail_break(self):
154
 
        error = errors.JailBreak("some url")
155
 
        self.assertEqualDiff("An attempt to access a url outside the server"
156
 
            " jail was made: 'some url'.",
157
 
            str(error))
 
110
    def test_install_failed(self):
 
111
        error = errors.InstallFailed(['rev-one'])
 
112
        self.assertEqual("Could not install revisions:\nrev-one", str(error))
 
113
        error = errors.InstallFailed(['rev-one', 'rev-two'])
 
114
        self.assertEqual("Could not install revisions:\nrev-one, rev-two",
 
115
                         str(error))
 
116
        error = errors.InstallFailed([None])
 
117
        self.assertEqual("Could not install revisions:\nNone", str(error))
158
118
 
159
119
    def test_lock_active(self):
160
120
        error = errors.LockActive("lock description")
162
122
            "cannot be broken.",
163
123
            str(error))
164
124
 
165
 
    def test_lock_corrupt(self):
166
 
        error = errors.LockCorrupt("corruption info")
167
 
        self.assertEqualDiff("Lock is apparently held, but corrupted: "
168
 
            "corruption info\n"
169
 
            "Use 'bzr break-lock' to clear it",
170
 
            str(error))
171
 
 
172
125
    def test_knit_data_stream_incompatible(self):
173
126
        error = errors.KnitDataStreamIncompatible(
174
127
            'stream format', 'target format')
198
151
        error = errors.MediumNotConnected("a medium")
199
152
        self.assertEqualDiff(
200
153
            "The medium 'a medium' is not connected.", str(error))
201
 
 
 
154
 
202
155
    def test_no_public_branch(self):
203
156
        b = self.make_branch('.')
204
157
        error = errors.NoPublicBranch(b)
207
160
            'There is no public branch set for "%s".' % url, str(error))
208
161
 
209
162
    def test_no_repo(self):
210
 
        dir = controldir.ControlDir.create(self.get_url())
 
163
        dir = bzrdir.BzrDir.create(self.get_url())
211
164
        error = errors.NoRepositoryPresent(dir)
212
165
        self.assertNotEqual(-1, str(error).find((dir.transport.clone('..').base)))
213
166
        self.assertEqual(-1, str(error).find((dir.transport.base)))
214
 
 
 
167
        
215
168
    def test_no_smart_medium(self):
216
169
        error = errors.NoSmartMedium("a transport")
217
170
        self.assertEqualDiff("The transport 'a transport' cannot tunnel the "
285
238
            "You will need to upgrade the branch to permit branch stacking.",
286
239
            str(error))
287
240
 
288
 
    def test_unstackable_location(self):
289
 
        error = errors.UnstackableLocationError('foo', 'bar')
290
 
        self.assertEqualDiff("The branch 'foo' cannot be stacked on 'bar'.",
291
 
            str(error))
292
 
 
293
241
    def test_unstackable_repository_format(self):
294
242
        format = u'foo'
295
243
        url = "/foo"
300
248
            str(error))
301
249
 
302
250
    def test_up_to_date(self):
303
 
        error = errors.UpToDateFormat("someformat")
304
 
        self.assertEqualDiff(
305
 
            "The branch format someformat is already at the most "
306
 
            "recent format.", str(error))
 
251
        error = errors.UpToDateFormat(bzrdir.BzrDirFormat4())
 
252
        self.assertEqualDiff("The branch format Bazaar-NG branch, "
 
253
                             "format 0.0.4 is already at the most "
 
254
                             "recent format.",
 
255
                             str(error))
307
256
 
308
257
    def test_corrupt_repository(self):
309
258
        repo = self.make_repository('.')
349
298
        self.assertEqual("The value 'foo' is not a valid value.",
350
299
            str(error))
351
300
 
 
301
    def test_bzrnewerror_is_deprecated(self):
 
302
        class DeprecatedError(errors.BzrNewError):
 
303
            pass
 
304
        self.callDeprecated(['BzrNewError was deprecated in bzr 0.13; '
 
305
             'please convert DeprecatedError to use BzrError instead'],
 
306
            DeprecatedError)
 
307
 
352
308
    def test_bzrerror_from_literal_string(self):
353
309
        # Some code constructs BzrError from a literal string, in which case
354
310
        # no further formatting is done.  (I'm not sure raising the base class
441
397
        """Test the formatting of MalformedBugIdentifier."""
442
398
        error = errors.MalformedBugIdentifier('bogus', 'reason for bogosity')
443
399
        self.assertEqual(
444
 
            'Did not understand bug identifier bogus: reason for bogosity. '
445
 
            'See "bzr help bugs" for more information on this feature.',
 
400
            "Did not understand bug identifier bogus: reason for bogosity",
446
401
            str(error))
447
402
 
448
403
    def test_unknown_bug_tracker_abbreviation(self):
499
454
        self.assertEqual(
500
455
            "Container has multiple records with the same name: n\xc3\xa5me",
501
456
            str(e))
502
 
 
 
457
        
503
458
    def test_check_error(self):
504
459
        # This has a member called 'message', which is problematic in
505
460
        # python2.5 because that is a slot on the base Exception class
517
472
 
518
473
    def test_immortal_pending_deletion_message(self):
519
474
        err = errors.ImmortalPendingDeletion('foo')
520
 
        self.assertEqual(
 
475
        self.assertEquals(
521
476
            "Unable to delete transform temporary directory foo.  "
522
477
            "Please examine foo to see if it contains any files "
523
478
            "you wish to keep, and delete it when you are done.",
525
480
 
526
481
    def test_unable_create_symlink(self):
527
482
        err = errors.UnableCreateSymlink()
528
 
        self.assertEqual(
 
483
        self.assertEquals(
529
484
            "Unable to create symlink on this platform",
530
485
            str(err))
531
486
        err = errors.UnableCreateSymlink(path=u'foo')
532
 
        self.assertEqual(
 
487
        self.assertEquals(
533
488
            "Unable to create symlink 'foo' on this platform",
534
489
            str(err))
535
490
        err = errors.UnableCreateSymlink(path=u'\xb5')
536
 
        self.assertEqual(
 
491
        self.assertEquals(
537
492
            "Unable to create symlink u'\\xb5' on this platform",
538
493
            str(err))
539
494
 
546
501
 
547
502
    def test_incorrect_url(self):
548
503
        err = errors.InvalidBugTrackerURL('foo', 'http://bug.com/')
549
 
        self.assertEqual(
 
504
        self.assertEquals(
550
505
            ("The URL for bug tracker \"foo\" doesn't contain {id}: "
551
506
             "http://bug.com/"),
552
507
            str(err))
553
508
 
554
509
    def test_unable_encode_path(self):
555
510
        err = errors.UnableEncodePath('foo', 'executable')
556
 
        self.assertEqual("Unable to encode executable path 'foo' in "
557
 
                         "user encoding " + osutils.get_user_encoding(),
558
 
                         str(err))
 
511
        self.assertEquals("Unable to encode executable path 'foo' in "
 
512
            "user encoding " + osutils.get_user_encoding(),
 
513
            str(err))
559
514
 
560
515
    def test_unknown_format(self):
561
516
        err = errors.UnknownFormatError('bar', kind='foo')
562
 
        self.assertEqual("Unknown foo format: 'bar'", str(err))
 
517
        self.assertEquals("Unknown foo format: 'bar'", str(err))
563
518
 
564
519
    def test_unknown_rules(self):
565
520
        err = errors.UnknownRules(['foo', 'bar'])
566
 
        self.assertEqual("Unknown rules detected: foo, bar.", str(err))
 
521
        self.assertEquals("Unknown rules detected: foo, bar.", str(err))
 
522
 
 
523
    def test_hook_failed(self):
 
524
        # Create an exc_info tuple by raising and catching an exception.
 
525
        try:
 
526
            1/0
 
527
        except ZeroDivisionError:
 
528
            exc_info = sys.exc_info()
 
529
        err = errors.HookFailed('hook stage', 'hook name', exc_info)
 
530
        self.assertStartsWith(
 
531
            str(err), 'Hook \'hook name\' during hook stage failed:\n')
 
532
        self.assertEndsWith(
 
533
            str(err), 'integer division or modulo by zero')
567
534
 
568
535
    def test_tip_change_rejected(self):
569
536
        err = errors.TipChangeRejected(u'Unicode message\N{INTERROBANG}')
570
 
        self.assertEqual(
 
537
        self.assertEquals(
571
538
            u'Tip change rejected: Unicode message\N{INTERROBANG}',
572
539
            unicode(err))
573
 
        self.assertEqual(
 
540
        self.assertEquals(
574
541
            'Tip change rejected: Unicode message\xe2\x80\xbd',
575
542
            str(err))
576
543
 
577
 
    def test_error_from_smart_server(self):
578
 
        error_tuple = ('error', 'tuple')
579
 
        err = errors.ErrorFromSmartServer(error_tuple)
580
 
        self.assertEqual(
581
 
            "Error received from smart server: ('error', 'tuple')", str(err))
582
 
 
583
 
    def test_untranslateable_error_from_smart_server(self):
584
 
        error_tuple = ('error', 'tuple')
585
 
        orig_err = errors.ErrorFromSmartServer(error_tuple)
586
 
        err = errors.UnknownErrorFromSmartServer(orig_err)
587
 
        self.assertEqual(
588
 
            "Server sent an unexpected error: ('error', 'tuple')", str(err))
589
 
 
590
 
    def test_smart_message_handler_error(self):
591
 
        # Make an exc_info tuple.
592
 
        try:
593
 
            raise Exception("example error")
594
 
        except Exception:
595
 
            err = errors.SmartMessageHandlerError(sys.exc_info())
596
 
        # GZ 2010-11-08: Should not store exc_info in exception instances.
597
 
        try:
598
 
            self.assertStartsWith(
599
 
                str(err), "The message handler raised an exception:\n")
600
 
            self.assertEndsWith(str(err), "Exception: example error\n")
601
 
        finally:
602
 
            del err
603
 
 
604
 
    def test_must_have_working_tree(self):
605
 
        err = errors.MustHaveWorkingTree('foo', 'bar')
606
 
        self.assertEqual(str(err), "Branching 'bar'(foo) must create a"
607
 
                                   " working tree.")
608
 
 
609
 
    def test_no_such_view(self):
610
 
        err = errors.NoSuchView('foo')
611
 
        self.assertEqual("No such view: foo.", str(err))
612
 
 
613
 
    def test_views_not_supported(self):
614
 
        err = errors.ViewsNotSupported('atree')
615
 
        err_str = str(err)
616
 
        self.assertStartsWith(err_str, "Views are not supported by ")
617
 
        self.assertEndsWith(err_str, "; use 'bzr upgrade' to change your "
618
 
            "tree to a later format.")
619
 
 
620
 
    def test_file_outside_view(self):
621
 
        err = errors.FileOutsideView('baz', ['foo', 'bar'])
622
 
        self.assertEqual('Specified file "baz" is outside the current view: '
623
 
                         'foo, bar', str(err))
624
 
 
625
 
    def test_invalid_shelf_id(self):
626
 
        invalid_id = "foo"
627
 
        err = errors.InvalidShelfId(invalid_id)
628
 
        self.assertEqual('"foo" is not a valid shelf id, '
629
 
                         'try a number instead.', str(err))
630
 
 
631
 
    def test_unresumable_write_group(self):
632
 
        repo = "dummy repo"
633
 
        wg_tokens = ['token']
634
 
        reason = "a reason"
635
 
        err = errors.UnresumableWriteGroup(repo, wg_tokens, reason)
636
 
        self.assertEqual(
637
 
            "Repository dummy repo cannot resume write group "
638
 
            "['token']: a reason", str(err))
639
 
 
640
 
    def test_unsuspendable_write_group(self):
641
 
        repo = "dummy repo"
642
 
        err = errors.UnsuspendableWriteGroup(repo)
643
 
        self.assertEqual(
644
 
            'Repository dummy repo cannot suspend a write group.', str(err))
645
 
 
646
 
    def test_not_branch_no_args(self):
647
 
        err = errors.NotBranchError('path')
648
 
        self.assertEqual('Not a branch: "path".', str(err))
649
 
 
650
 
    def test_not_branch_bzrdir_with_repo(self):
651
 
        bzrdir = self.make_repository('repo').bzrdir
652
 
        err = errors.NotBranchError('path', bzrdir=bzrdir)
653
 
        self.assertEqual(
654
 
            'Not a branch: "path": location is a repository.', str(err))
655
 
 
656
 
    def test_not_branch_bzrdir_without_repo(self):
657
 
        bzrdir = self.make_bzrdir('bzrdir')
658
 
        err = errors.NotBranchError('path', bzrdir=bzrdir)
659
 
        self.assertEqual('Not a branch: "path".', str(err))
660
 
 
661
 
    def test_not_branch_bzrdir_with_recursive_not_branch_error(self):
662
 
        class FakeBzrDir(object):
663
 
            def open_repository(self):
664
 
                # str() on the NotBranchError will trigger a call to this,
665
 
                # which in turn will another, identical NotBranchError.
666
 
                raise errors.NotBranchError('path', bzrdir=FakeBzrDir())
667
 
        err = errors.NotBranchError('path', bzrdir=FakeBzrDir())
668
 
        self.assertEqual('Not a branch: "path".', str(err))
669
 
 
670
 
    def test_not_branch_laziness(self):
671
 
        real_bzrdir = self.make_bzrdir('path')
672
 
        class FakeBzrDir(object):
673
 
            def __init__(self):
674
 
                self.calls = []
675
 
            def open_repository(self):
676
 
                self.calls.append('open_repository')
677
 
                raise errors.NoRepositoryPresent(real_bzrdir)
678
 
        fake_bzrdir = FakeBzrDir()
679
 
        err = errors.NotBranchError('path', bzrdir=fake_bzrdir)
680
 
        self.assertEqual([], fake_bzrdir.calls)
681
 
        str(err)
682
 
        self.assertEqual(['open_repository'], fake_bzrdir.calls)
683
 
        # Stringifying twice doesn't try to open a repository twice.
684
 
        str(err)
685
 
        self.assertEqual(['open_repository'], fake_bzrdir.calls)
686
 
 
687
 
    def test_invalid_pattern(self):
688
 
        error = errors.InvalidPattern('Bad pattern msg.')
689
 
        self.assertEqualDiff("Invalid pattern(s) found. Bad pattern msg.",
690
 
            str(error))
691
 
 
692
 
    def test_recursive_bind(self):
693
 
        error = errors.RecursiveBind('foo_bar_branch')
694
 
        msg = ('Branch "foo_bar_branch" appears to be bound to itself. '
695
 
            'Please use `bzr unbind` to fix.')
696
 
        self.assertEqualDiff(msg, str(error))
697
 
 
698
 
    def test_retry_with_new_packs(self):
699
 
        fake_exc_info = ('{exc type}', '{exc value}', '{exc traceback}')
700
 
        error = errors.RetryWithNewPacks(
701
 
            '{context}', reload_occurred=False, exc_info=fake_exc_info)
702
 
        self.assertEqual(
703
 
            'Pack files have changed, reload and retry. context: '
704
 
            '{context} {exc value}', str(error))
705
 
 
706
544
 
707
545
class PassThroughError(errors.BzrError):
708
 
 
 
546
    
709
547
    _fmt = """Pass through %(foo)s and %(bar)s"""
710
548
 
711
549
    def __init__(self, foo, bar):
718
556
 
719
557
 
720
558
class ErrorWithNoFormat(errors.BzrError):
721
 
    __doc__ = """This class has a docstring but no format string."""
 
559
    """This class has a docstring but no format string."""
722
560
 
723
561
 
724
562
class TestErrorFormatting(TestCase):
725
 
 
 
563
    
726
564
    def test_always_str(self):
727
565
        e = PassThroughError(u'\xb5', 'bar')
728
566
        self.assertIsInstance(e.__str__(), str)
735
573
 
736
574
    def test_missing_format_string(self):
737
575
        e = ErrorWithNoFormat(param='randomvalue')
738
 
        self.assertStartsWith(str(e),
739
 
                              "Unprintable exception ErrorWithNoFormat")
 
576
        s = self.callDeprecated(
 
577
                ['ErrorWithNoFormat uses its docstring as a format, it should use _fmt instead'],
 
578
                lambda x: str(x), e)
 
579
        ## s = str(e)
 
580
        self.assertEqual(s, 
 
581
                "This class has a docstring but no format string.")
740
582
 
741
583
    def test_mismatched_format_args(self):
742
584
        # Even though ErrorWithBadFormat's format string does not match the
745
587
        e = ErrorWithBadFormat(not_thing='x')
746
588
        self.assertStartsWith(
747
589
            str(e), 'Unprintable exception ErrorWithBadFormat')
748
 
 
749
 
    def test_cannot_bind_address(self):
750
 
        # see <https://bugs.launchpad.net/bzr/+bug/286871>
751
 
        e = errors.CannotBindAddress('example.com', 22,
752
 
                                     socket.error(13, 'Permission denied'))
753
 
        self.assertContainsRe(
754
 
            str(e),
755
 
            r'Cannot bind address "example\.com:22":.*Permission denied')
756
 
 
757
 
    def test_file_timestamp_unavailable(self):
758
 
        e = errors.FileTimestampUnavailable("/path/foo")
759
 
        self.assertEqual("The filestamp for /path/foo is not available.",
760
 
                         str(e))
761
 
 
762
 
    def test_transform_rename_failed(self):
763
 
        e = errors.TransformRenameFailed(u"from", u"to", "readonly file", 2)
764
 
        self.assertEqual(
765
 
            u"Failed to rename from to to: readonly file",
766
 
            str(e))