18
19
"""Tests for the formatting and construction of errors."""
20
import bzrlib.bzrdir as bzrdir
21
import bzrlib.errors as errors
22
from bzrlib.tests import TestCaseWithTransport
28
from bzrlib.tests import TestCase, TestCaseWithTransport
25
31
class TestErrors(TestCaseWithTransport):
33
def test_bad_filename_encoding(self):
34
error = errors.BadFilenameEncoding('bad/filen\xe5me', 'UTF-8')
36
"Filename 'bad/filen\\xe5me' is not valid in your current"
37
" filesystem encoding UTF-8",
40
def test_corrupt_dirstate(self):
41
error = errors.CorruptDirstate('path/to/dirstate', 'the reason why')
43
"Inconsistency in dirstate file path/to/dirstate.\n"
44
"Error: the reason why",
47
def test_disabled_method(self):
48
error = errors.DisabledMethod("class name")
50
"The smart server method 'class name' is disabled.", str(error))
52
def test_duplicate_file_id(self):
53
error = errors.DuplicateFileId('a_file_id', 'foo')
54
self.assertEqualDiff('File id {a_file_id} already exists in inventory'
55
' as foo', str(error))
57
def test_duplicate_help_prefix(self):
58
error = errors.DuplicateHelpPrefix('foo')
59
self.assertEqualDiff('The prefix foo is in the help search path twice.',
62
def test_ghost_revisions_have_no_revno(self):
63
error = errors.GhostRevisionsHaveNoRevno('target', 'ghost_rev')
64
self.assertEqualDiff("Could not determine revno for {target} because"
65
" its ancestry shows a ghost at {ghost_rev}",
68
def test_incompatibleAPI(self):
69
error = errors.IncompatibleAPI("module", (1, 2, 3), (4, 5, 6), (7, 8, 9))
71
'The API for "module" is not compatible with "(1, 2, 3)". '
72
'It supports versions "(4, 5, 6)" to "(7, 8, 9)".',
75
def test_inconsistent_delta(self):
76
error = errors.InconsistentDelta('path', 'file-id', 'reason for foo')
78
"An inconsistent delta was supplied involving 'path', 'file-id'\n"
79
"reason: reason for foo",
82
def test_in_process_transport(self):
83
error = errors.InProcessTransport('fpp')
85
"The transport 'fpp' is only accessible within this process.",
88
def test_invalid_http_range(self):
89
error = errors.InvalidHttpRange('path',
90
'Content-Range: potatoes 0-00/o0oo0',
92
self.assertEquals("Invalid http range"
93
" 'Content-Range: potatoes 0-00/o0oo0'"
94
" for path: bad range",
97
def test_invalid_range(self):
98
error = errors.InvalidRange('path', 12, 'bad range')
99
self.assertEquals("Invalid range access in path at 12: bad range",
102
def test_inventory_modified(self):
103
error = errors.InventoryModified("a tree to be repred")
104
self.assertEqualDiff("The current inventory for the tree 'a tree to "
105
"be repred' has been modified, so a clean inventory cannot be "
106
"read without data loss.",
109
def test_install_failed(self):
110
error = errors.InstallFailed(['rev-one'])
111
self.assertEqual("Could not install revisions:\nrev-one", str(error))
112
error = errors.InstallFailed(['rev-one', 'rev-two'])
113
self.assertEqual("Could not install revisions:\nrev-one, rev-two",
115
error = errors.InstallFailed([None])
116
self.assertEqual("Could not install revisions:\nNone", str(error))
118
def test_lock_active(self):
119
error = errors.LockActive("lock description")
120
self.assertEqualDiff("The lock for 'lock description' is in use and "
124
def test_knit_data_stream_incompatible(self):
125
error = errors.KnitDataStreamIncompatible(
126
'stream format', 'target format')
127
self.assertEqual('Cannot insert knit data stream of format '
128
'"stream format" into knit of format '
129
'"target format".', str(error))
131
def test_knit_data_stream_unknown(self):
132
error = errors.KnitDataStreamUnknown(
134
self.assertEqual('Cannot parse knit data stream of format '
135
'"stream format".', str(error))
137
def test_knit_header_error(self):
138
error = errors.KnitHeaderError('line foo\n', 'path/to/file')
139
self.assertEqual("Knit header error: 'line foo\\n' unexpected"
140
" for file \"path/to/file\".", str(error))
142
def test_knit_index_unknown_method(self):
143
error = errors.KnitIndexUnknownMethod('http://host/foo.kndx',
145
self.assertEqual("Knit index http://host/foo.kndx does not have a"
146
" known method in options: ['bad', 'no-eol']",
149
def test_medium_not_connected(self):
150
error = errors.MediumNotConnected("a medium")
151
self.assertEqualDiff(
152
"The medium 'a medium' is not connected.", str(error))
154
def test_no_public_branch(self):
155
b = self.make_branch('.')
156
error = errors.NoPublicBranch(b)
157
url = urlutils.unescape_for_display(b.base, 'ascii')
158
self.assertEqualDiff(
159
'There is no public branch set for "%s".' % url, str(error))
27
161
def test_no_repo(self):
28
162
dir = bzrdir.BzrDir.create(self.get_url())
29
163
error = errors.NoRepositoryPresent(dir)
30
self.assertNotEqual(-1, str(error).find(repr(dir.transport.clone('..').base)))
31
self.assertEqual(-1, str(error).find(repr(dir.transport.base)))
164
self.assertNotEqual(-1, str(error).find((dir.transport.clone('..').base)))
165
self.assertEqual(-1, str(error).find((dir.transport.base)))
167
def test_no_smart_medium(self):
168
error = errors.NoSmartMedium("a transport")
169
self.assertEqualDiff("The transport 'a transport' cannot tunnel the "
173
def test_no_help_topic(self):
174
error = errors.NoHelpTopic("topic")
175
self.assertEqualDiff("No help could be found for 'topic'. "
176
"Please use 'bzr help topics' to obtain a list of topics.",
179
def test_no_such_id(self):
180
error = errors.NoSuchId("atree", "anid")
181
self.assertEqualDiff("The file id \"anid\" is not present in the tree "
185
def test_no_such_revision_in_tree(self):
186
error = errors.NoSuchRevisionInTree("atree", "anid")
187
self.assertEqualDiff("The revision id {anid} is not present in the"
188
" tree atree.", str(error))
189
self.assertIsInstance(error, errors.NoSuchRevision)
191
def test_not_stacked(self):
192
error = errors.NotStacked('a branch')
193
self.assertEqualDiff("The branch 'a branch' is not stacked.",
196
def test_not_write_locked(self):
197
error = errors.NotWriteLocked('a thing to repr')
198
self.assertEqualDiff("'a thing to repr' is not write locked but needs "
202
def test_lock_failed(self):
203
error = errors.LockFailed('http://canonical.com/', 'readonly transport')
204
self.assertEqualDiff("Cannot lock http://canonical.com/: readonly transport",
206
self.assertFalse(error.internal_error)
208
def test_too_many_concurrent_requests(self):
209
error = errors.TooManyConcurrentRequests("a medium")
210
self.assertEqualDiff("The medium 'a medium' has reached its concurrent "
211
"request limit. Be sure to finish_writing and finish_reading on "
212
"the currently open request.",
215
def test_unavailable_representation(self):
216
error = errors.UnavailableRepresentation(('key',), "mpdiff", "fulltext")
217
self.assertEqualDiff("The encoding 'mpdiff' is not available for key "
218
"('key',) which is encoded as 'fulltext'.",
221
def test_unknown_hook(self):
222
error = errors.UnknownHook("branch", "foo")
223
self.assertEqualDiff("The branch hook 'foo' is unknown in this version"
226
error = errors.UnknownHook("tree", "bar")
227
self.assertEqualDiff("The tree hook 'bar' is unknown in this version"
231
def test_unstackable_branch_format(self):
234
error = errors.UnstackableBranchFormat(format, url)
235
self.assertEqualDiff(
236
"The branch '/foo'(foo) is not a stackable format. "
237
"You will need to upgrade the branch to permit branch stacking.",
240
def test_unstackable_repository_format(self):
243
error = errors.UnstackableRepositoryFormat(format, url)
244
self.assertEqualDiff(
245
"The repository '/foo'(foo) is not a stackable format. "
246
"You will need to upgrade the repository to permit branch stacking.",
33
249
def test_up_to_date(self):
34
250
error = errors.UpToDateFormat(bzrdir.BzrDirFormat4())
44
260
"Please run bzr reconcile on this repository." %
45
261
repo.bzrdir.root_transport.base,
264
def test_read_error(self):
265
# a unicode path to check that %r is being used.
267
error = errors.ReadError(path)
268
self.assertEqualDiff("Error reading from u'a path'.", str(error))
270
def test_bad_index_format_signature(self):
271
error = errors.BadIndexFormatSignature("foo", "bar")
272
self.assertEqual("foo is not an index of type bar.",
275
def test_bad_index_data(self):
276
error = errors.BadIndexData("foo")
277
self.assertEqual("Error in data for index foo.",
280
def test_bad_index_duplicate_key(self):
281
error = errors.BadIndexDuplicateKey("foo", "bar")
282
self.assertEqual("The key 'foo' is already in index 'bar'.",
285
def test_bad_index_key(self):
286
error = errors.BadIndexKey("foo")
287
self.assertEqual("The key 'foo' is not a valid key.",
290
def test_bad_index_options(self):
291
error = errors.BadIndexOptions("foo")
292
self.assertEqual("Could not parse options for index foo.",
295
def test_bad_index_value(self):
296
error = errors.BadIndexValue("foo")
297
self.assertEqual("The value 'foo' is not a valid value.",
300
def test_bzrnewerror_is_deprecated(self):
301
class DeprecatedError(errors.BzrNewError):
303
self.callDeprecated(['BzrNewError was deprecated in bzr 0.13; '
304
'please convert DeprecatedError to use BzrError instead'],
307
def test_bzrerror_from_literal_string(self):
308
# Some code constructs BzrError from a literal string, in which case
309
# no further formatting is done. (I'm not sure raising the base class
310
# is a great idea, but if the exception is not intended to be caught
311
# perhaps no more is needed.)
313
raise errors.BzrError('this is my errors; %d is not expanded')
314
except errors.BzrError, e:
315
self.assertEqual('this is my errors; %d is not expanded', str(e))
317
def test_reading_completed(self):
318
error = errors.ReadingCompleted("a request")
319
self.assertEqualDiff("The MediumRequest 'a request' has already had "
320
"finish_reading called upon it - the request has been completed and"
321
" no more data may be read.",
324
def test_writing_completed(self):
325
error = errors.WritingCompleted("a request")
326
self.assertEqualDiff("The MediumRequest 'a request' has already had "
327
"finish_writing called upon it - accept bytes may not be called "
331
def test_writing_not_completed(self):
332
error = errors.WritingNotComplete("a request")
333
self.assertEqualDiff("The MediumRequest 'a request' has not has "
334
"finish_writing called upon it - until the write phase is complete"
335
" no data may be read.",
338
def test_transport_not_possible(self):
339
error = errors.TransportNotPossible('readonly', 'original error')
340
self.assertEqualDiff('Transport operation not possible:'
341
' readonly original error', str(error))
343
def assertSocketConnectionError(self, expected, *args, **kwargs):
344
"""Check the formatting of a SocketConnectionError exception"""
345
e = errors.SocketConnectionError(*args, **kwargs)
346
self.assertEqual(expected, str(e))
348
def test_socket_connection_error(self):
349
"""Test the formatting of SocketConnectionError"""
351
# There should be a default msg about failing to connect
352
# we only require a host name.
353
self.assertSocketConnectionError(
354
'Failed to connect to ahost',
357
# If port is None, we don't put :None
358
self.assertSocketConnectionError(
359
'Failed to connect to ahost',
361
# But if port is supplied we include it
362
self.assertSocketConnectionError(
363
'Failed to connect to ahost:22',
366
# We can also supply extra information about the error
367
# with or without a port
368
self.assertSocketConnectionError(
369
'Failed to connect to ahost:22; bogus error',
370
'ahost', port=22, orig_error='bogus error')
371
self.assertSocketConnectionError(
372
'Failed to connect to ahost; bogus error',
373
'ahost', orig_error='bogus error')
374
# An exception object can be passed rather than a string
375
orig_error = ValueError('bad value')
376
self.assertSocketConnectionError(
377
'Failed to connect to ahost; %s' % (str(orig_error),),
378
host='ahost', orig_error=orig_error)
380
# And we can supply a custom failure message
381
self.assertSocketConnectionError(
382
'Unable to connect to ssh host ahost:444; my_error',
383
host='ahost', port=444, msg='Unable to connect to ssh host',
384
orig_error='my_error')
386
def test_malformed_bug_identifier(self):
387
"""Test the formatting of MalformedBugIdentifier."""
388
error = errors.MalformedBugIdentifier('bogus', 'reason for bogosity')
390
"Did not understand bug identifier bogus: reason for bogosity",
393
def test_unknown_bug_tracker_abbreviation(self):
394
"""Test the formatting of UnknownBugTrackerAbbreviation."""
395
branch = self.make_branch('some_branch')
396
error = errors.UnknownBugTrackerAbbreviation('xxx', branch)
398
"Cannot find registered bug tracker called xxx on %s" % branch,
401
def test_unexpected_smart_server_response(self):
402
e = errors.UnexpectedSmartServerResponse(('not yes',))
404
"Could not understand response from smart server: ('not yes',)",
407
def test_unknown_container_format(self):
408
"""Test the formatting of UnknownContainerFormatError."""
409
e = errors.UnknownContainerFormatError('bad format string')
411
"Unrecognised container format: 'bad format string'",
414
def test_unexpected_end_of_container(self):
415
"""Test the formatting of UnexpectedEndOfContainerError."""
416
e = errors.UnexpectedEndOfContainerError()
418
"Unexpected end of container stream", str(e))
420
def test_unknown_record_type(self):
421
"""Test the formatting of UnknownRecordTypeError."""
422
e = errors.UnknownRecordTypeError("X")
424
"Unknown record type: 'X'",
427
def test_invalid_record(self):
428
"""Test the formatting of InvalidRecordError."""
429
e = errors.InvalidRecordError("xxx")
431
"Invalid record: xxx",
434
def test_container_has_excess_data(self):
435
"""Test the formatting of ContainerHasExcessDataError."""
436
e = errors.ContainerHasExcessDataError("excess bytes")
438
"Container has data after end marker: 'excess bytes'",
441
def test_duplicate_record_name_error(self):
442
"""Test the formatting of DuplicateRecordNameError."""
443
e = errors.DuplicateRecordNameError(u"n\xe5me".encode('utf-8'))
445
"Container has multiple records with the same name: n\xc3\xa5me",
448
def test_check_error(self):
449
# This has a member called 'message', which is problematic in
450
# python2.5 because that is a slot on the base Exception class
451
e = errors.BzrCheckError('example check failure')
453
"Internal check failed: example check failure",
455
self.assertTrue(e.internal_error)
457
def test_repository_data_stream_error(self):
458
"""Test the formatting of RepositoryDataStreamError."""
459
e = errors.RepositoryDataStreamError(u"my reason")
461
"Corrupt or incompatible data stream: my reason", str(e))
463
def test_immortal_pending_deletion_message(self):
464
err = errors.ImmortalPendingDeletion('foo')
466
"Unable to delete transform temporary directory foo. "
467
"Please examine foo to see if it contains any files "
468
"you wish to keep, and delete it when you are done.",
471
def test_unable_create_symlink(self):
472
err = errors.UnableCreateSymlink()
474
"Unable to create symlink on this platform",
476
err = errors.UnableCreateSymlink(path=u'foo')
478
"Unable to create symlink 'foo' on this platform",
480
err = errors.UnableCreateSymlink(path=u'\xb5')
482
"Unable to create symlink u'\\xb5' on this platform",
485
def test_invalid_url_join(self):
486
"""Test the formatting of InvalidURLJoin."""
487
e = errors.InvalidURLJoin('Reason', 'base path', ('args',))
489
"Invalid URL join request: Reason: 'base path' + ('args',)",
492
def test_incorrect_url(self):
493
err = errors.InvalidBugTrackerURL('foo', 'http://bug.com/')
495
("The URL for bug tracker \"foo\" doesn't contain {id}: "
499
def test_unable_encode_path(self):
500
err = errors.UnableEncodePath('foo', 'executable')
501
self.assertEquals("Unable to encode executable path 'foo' in "
502
"user encoding " + osutils.get_user_encoding(),
505
def test_unknown_format(self):
506
err = errors.UnknownFormatError('bar', kind='foo')
507
self.assertEquals("Unknown foo format: 'bar'", str(err))
509
def test_unknown_rules(self):
510
err = errors.UnknownRules(['foo', 'bar'])
511
self.assertEquals("Unknown rules detected: foo, bar.", str(err))
514
class PassThroughError(errors.BzrError):
516
_fmt = """Pass through %(foo)s and %(bar)s"""
518
def __init__(self, foo, bar):
519
errors.BzrError.__init__(self, foo=foo, bar=bar)
522
class ErrorWithBadFormat(errors.BzrError):
524
_fmt = """One format specifier: %(thing)s"""
527
class ErrorWithNoFormat(errors.BzrError):
528
"""This class has a docstring but no format string."""
531
class TestErrorFormatting(TestCase):
533
def test_always_str(self):
534
e = PassThroughError(u'\xb5', 'bar')
535
self.assertIsInstance(e.__str__(), str)
536
# In Python str(foo) *must* return a real byte string
537
# not a Unicode string. The following line would raise a
538
# Unicode error, because it tries to call str() on the string
539
# returned from e.__str__(), and it has non ascii characters
541
self.assertEqual('Pass through \xc2\xb5 and bar', s)
543
def test_missing_format_string(self):
544
e = ErrorWithNoFormat(param='randomvalue')
545
s = self.callDeprecated(
546
['ErrorWithNoFormat uses its docstring as a format, it should use _fmt instead'],
550
"This class has a docstring but no format string.")
552
def test_mismatched_format_args(self):
553
# Even though ErrorWithBadFormat's format string does not match the
554
# arguments we constructing it with, we can still stringify an instance
555
# of this exception. The resulting string will say its unprintable.
556
e = ErrorWithBadFormat(not_thing='x')
557
self.assertStartsWith(
558
str(e), 'Unprintable exception ErrorWithBadFormat')