Status: | Merged |
---|---|
Approved by: | Martin Packman |
Approved revision: | no longer in the source branch. |
Merge reported by: | The Breezy Bot |
Merged at revision: | not available |
Proposed branch: | lp:~gz/brz/py3_errors |
Merge into: | lp:brz |
Diff against target: |
593 lines (+158/-154) 4 files modified
breezy/errors.py (+41/-47) breezy/inventory_delta.py (+34/-20) breezy/tests/test_errors.py (+78/-84) breezy/tests/test_inventory_delta.py (+5/-3) |
To merge this branch: | bzr merge lp:~gz/brz/py3_errors |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jelmer Vernooij | Approve | ||
Review via email: mp+325347@code.launchpad.net |
Commit message
Improve Python 3 compatibility of errors module
Description of the change
Selection of changes that move breezy.errors towards Python 3 compatibility.
Implement __hash__ which is now required on classes that have an __eq__.
Only provide __str__/__unicode__ methods as appropriate.
In the tests, moved out errors that actually require a transport to a separate class, and fix a few other suspect assertions.
To post a comment you must log in.
Revision history for this message
Jelmer Vernooij (jelmer) : | # |
review:
Approve
Revision history for this message
The Breezy Bot (the-breezy-bot) wrote : | # |
Revision history for this message
Martin Packman (gz) wrote : | # |
Ended up doing rather more changed than intended to inventory_delta based on the one test failure there, so this branch could probably do with another quick review.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'breezy/errors.py' |
2 | --- breezy/errors.py 2017-05-22 00:56:52 +0000 |
3 | +++ breezy/errors.py 2017-06-09 16:32:43 +0000 |
4 | @@ -19,6 +19,10 @@ |
5 | |
6 | from __future__ import absolute_import |
7 | |
8 | +from .sixish import ( |
9 | + PY3, |
10 | + ) |
11 | + |
12 | # TODO: is there any value in providing the .args field used by standard |
13 | # python exceptions? A list of values with no names seems less useful |
14 | # to me. |
15 | @@ -87,6 +91,7 @@ |
16 | if s is not None: |
17 | # contains a preformatted message |
18 | return s |
19 | + err = None |
20 | try: |
21 | fmt = self._get_format_string() |
22 | if fmt: |
23 | @@ -96,34 +101,20 @@ |
24 | # never a 'unicode' object. |
25 | return s |
26 | except Exception as e: |
27 | - pass # just bind to 'e' for formatting below |
28 | - else: |
29 | - e = None |
30 | + err = e |
31 | return 'Unprintable exception %s: dict=%r, fmt=%r, error=%r' \ |
32 | % (self.__class__.__name__, |
33 | self.__dict__, |
34 | getattr(self, '_fmt', None), |
35 | - e) |
36 | - |
37 | - def __unicode__(self): |
38 | - u = self._format() |
39 | - if isinstance(u, str): |
40 | - # Try decoding the str using the default encoding. |
41 | - u = unicode(u) |
42 | - elif not isinstance(u, unicode): |
43 | - # Try to make a unicode object from it, because __unicode__ must |
44 | - # return a unicode object. |
45 | - u = unicode(u) |
46 | - return u |
47 | - |
48 | - def __str__(self): |
49 | - s = self._format() |
50 | - if isinstance(s, unicode): |
51 | - s = s.encode('utf8') |
52 | - else: |
53 | - # __str__ must return a str. |
54 | - s = str(s) |
55 | - return s |
56 | + err) |
57 | + |
58 | + if PY3: |
59 | + __str__ = _format |
60 | + else: |
61 | + def __str__(self): |
62 | + return self._format().encode('utf-8') |
63 | + |
64 | + __unicode__ = _format |
65 | |
66 | def __repr__(self): |
67 | return '%s(%s)' % (self.__class__.__name__, str(self)) |
68 | @@ -133,13 +124,16 @@ |
69 | fmt = getattr(self, '_fmt', None) |
70 | if fmt is not None: |
71 | from breezy.i18n import gettext |
72 | - return gettext(unicode(fmt)) # _fmt strings should be ascii |
73 | + return gettext(fmt) # _fmt strings should be ascii |
74 | |
75 | def __eq__(self, other): |
76 | if self.__class__ is not other.__class__: |
77 | return NotImplemented |
78 | return self.__dict__ == other.__dict__ |
79 | |
80 | + def __hash__(self): |
81 | + return id(self) |
82 | + |
83 | |
84 | class InternalBzrError(BzrError): |
85 | """Base class for errors that are internal in nature. |
86 | @@ -665,30 +659,30 @@ |
87 | def __repr__(self): |
88 | return '<%s %r>' % (self.__class__.__name__, self.__dict__) |
89 | |
90 | - def _format(self): |
91 | - # XXX: Ideally self.detail would be a property, but Exceptions in |
92 | - # Python 2.4 have to be old-style classes so properties don't work. |
93 | - # Instead we override _format. |
94 | + def _get_format_string(self): |
95 | + # GZ 2017-06-08: Not the best place to lazy fill detail in. |
96 | if self.detail is None: |
97 | - if self.bzrdir is not None: |
98 | - try: |
99 | - self.bzrdir.open_repository() |
100 | - except NoRepositoryPresent: |
101 | - self.detail = '' |
102 | - except Exception: |
103 | - # Just ignore unexpected errors. Raising arbitrary errors |
104 | - # during str(err) can provoke strange bugs. Concretely |
105 | - # Launchpad's codehosting managed to raise NotBranchError |
106 | - # here, and then get stuck in an infinite loop/recursion |
107 | - # trying to str() that error. All this error really cares |
108 | - # about that there's no working repository there, and if |
109 | - # open_repository() fails, there probably isn't. |
110 | - self.detail = '' |
111 | - else: |
112 | - self.detail = ': location is a repository' |
113 | + self.detail = self._get_detail() |
114 | + return super(NotBranchError, self)._get_format_string() |
115 | + |
116 | + def _get_detail(self): |
117 | + if self.bzrdir is not None: |
118 | + try: |
119 | + self.bzrdir.open_repository() |
120 | + except NoRepositoryPresent: |
121 | + return '' |
122 | + except Exception as e: |
123 | + # Just ignore unexpected errors. Raising arbitrary errors |
124 | + # during str(err) can provoke strange bugs. Concretely |
125 | + # Launchpad's codehosting managed to raise NotBranchError |
126 | + # here, and then get stuck in an infinite loop/recursion |
127 | + # trying to str() that error. All this error really cares |
128 | + # about that there's no working repository there, and if |
129 | + # open_repository() fails, there probably isn't. |
130 | + return ': ' + e.__class__.__name__ |
131 | else: |
132 | - self.detail = '' |
133 | - return PathError._format(self) |
134 | + return ': location is a repository' |
135 | + return '' |
136 | |
137 | |
138 | class NoSubmitBranch(PathError): |
139 | |
140 | === modified file 'breezy/inventory_delta.py' |
141 | --- breezy/inventory_delta.py 2017-05-25 01:35:55 +0000 |
142 | +++ breezy/inventory_delta.py 2017-06-09 16:32:43 +0000 |
143 | @@ -36,12 +36,17 @@ |
144 | |
145 | class InventoryDeltaError(errors.BzrError): |
146 | """An error when serializing or deserializing an inventory delta.""" |
147 | - |
148 | + |
149 | # Most errors when serializing and deserializing are due to bugs, although |
150 | # damaged input (i.e. a bug in a different process) could cause |
151 | # deserialization errors too. |
152 | internal_error = True |
153 | |
154 | + def __init__(self, format_string, **kwargs): |
155 | + # Let each error supply a custom format string and arguments. |
156 | + self._fmt = format_string |
157 | + super(InventoryDeltaError, self).__init__(**kwargs) |
158 | + |
159 | |
160 | class IncompatibleInventoryDelta(errors.BzrError): |
161 | """The delta could not be deserialised because its contents conflict with |
162 | @@ -70,7 +75,8 @@ |
163 | exec_bytes = '' |
164 | size_exec_sha = (entry.text_size, exec_bytes, entry.text_sha1) |
165 | if None in size_exec_sha: |
166 | - raise InventoryDeltaError('Missing size or sha for %s' % entry.file_id) |
167 | + raise InventoryDeltaError( |
168 | + 'Missing size or sha for %(fileid)r', fileid=entry.file_id) |
169 | return "file\x00%d\x00%s\x00%s" % size_exec_sha |
170 | |
171 | |
172 | @@ -81,7 +87,8 @@ |
173 | """ |
174 | target = entry.symlink_target |
175 | if target is None: |
176 | - raise InventoryDeltaError('Missing target for %s' % entry.file_id) |
177 | + raise InventoryDeltaError( |
178 | + 'Missing target for %(fileid)r', fileid=entry.file_id) |
179 | return "link\x00%s" % target.encode('utf8') |
180 | |
181 | |
182 | @@ -93,7 +100,7 @@ |
183 | tree_revision = entry.reference_revision |
184 | if tree_revision is None: |
185 | raise InventoryDeltaError( |
186 | - 'Missing reference revision for %s' % entry.file_id) |
187 | + 'Missing reference revision for %(fileid)r', fileid=entry.file_id) |
188 | return "tree\x00%s" % tree_revision |
189 | |
190 | |
191 | @@ -180,9 +187,10 @@ |
192 | to_line = self._delta_item_to_line |
193 | for delta_item in delta_to_new: |
194 | line = to_line(delta_item, new_name) |
195 | - if line.__class__ != str: |
196 | + # GZ 2017-06-09: Not really worth asserting this here |
197 | + if line.__class__ != bytes: |
198 | raise InventoryDeltaError( |
199 | - 'to_line generated non-str output %r' % lines[-1]) |
200 | + 'to_line gave non-bytes output %(line)r', line=lines[-1]) |
201 | lines.append(line) |
202 | lines.sort() |
203 | lines[0] = "format: %s\n" % FORMAT_1 |
204 | @@ -236,10 +244,12 @@ |
205 | # xml5 serializer. |
206 | if last_modified != new_version: |
207 | raise InventoryDeltaError( |
208 | - 'Version present for / in %s (%s != %s)' |
209 | - % (file_id, last_modified, new_version)) |
210 | + 'Version present for / in %(fileid)r' |
211 | + ' (%(last)r != %(new)r)', |
212 | + fileid=file_id, last=last_modified, new=new_version) |
213 | if last_modified is None: |
214 | - raise InventoryDeltaError("no version for fileid %s" % file_id) |
215 | + raise InventoryDeltaError( |
216 | + "no version for fileid %(fileid)r", fileid=file_id) |
217 | content = self._entry_to_content[entry.kind](entry) |
218 | return ("%s\x00%s\x00%s\x00%s\x00%s\x00%s\n" % |
219 | (oldpath_utf8, newpath_utf8, file_id, parent_id, last_modified, |
220 | @@ -265,7 +275,7 @@ |
221 | elif value == "false": |
222 | return False |
223 | else: |
224 | - raise InventoryDeltaError("value %r is not a bool" % (value,)) |
225 | + raise InventoryDeltaError("value %(val)r is not a bool", val=value) |
226 | |
227 | def parse_text_bytes(self, bytes): |
228 | """Parse the text bytes of a serialized inventory delta. |
229 | @@ -281,10 +291,12 @@ |
230 | """ |
231 | if bytes[-1:] != '\n': |
232 | last_line = bytes.rsplit('\n', 1)[-1] |
233 | - raise InventoryDeltaError('last line not empty: %r' % (last_line,)) |
234 | + raise InventoryDeltaError( |
235 | + 'last line not empty: %(line)r', line=last_line) |
236 | lines = bytes.split('\n')[:-1] # discard the last empty line |
237 | if not lines or lines[0] != 'format: %s' % FORMAT_1: |
238 | - raise InventoryDeltaError('unknown format %r' % lines[0:1]) |
239 | + raise InventoryDeltaError( |
240 | + 'unknown format %(line)r', line=lines[0:1]) |
241 | if len(lines) < 2 or not lines[1].startswith('parent: '): |
242 | raise InventoryDeltaError('missing parent: marker') |
243 | delta_parent_id = lines[1][8:] |
244 | @@ -310,22 +322,24 @@ |
245 | parent_id = parent_id or None |
246 | if file_id in seen_ids: |
247 | raise InventoryDeltaError( |
248 | - "duplicate file id in inventory delta %r" % lines) |
249 | + "duplicate file id %(fileid)r", fileid=file_id) |
250 | seen_ids.add(file_id) |
251 | if (newpath_utf8 == '/' and not delta_versioned_root and |
252 | last_modified != delta_version_id): |
253 | # Delta claims to be not have a versioned root, yet here's |
254 | # a root entry with a non-default version. |
255 | - raise InventoryDeltaError("Versioned root found: %r" % line) |
256 | + raise InventoryDeltaError( |
257 | + "Versioned root found: %(line)r", line=line) |
258 | elif newpath_utf8 != 'None' and last_modified[-1] == ':': |
259 | # Deletes have a last_modified of null:, but otherwise special |
260 | # revision ids should not occur. |
261 | - raise InventoryDeltaError('special revisionid found: %r' % line) |
262 | + raise InventoryDeltaError( |
263 | + 'special revisionid found: %(line)r', line=line) |
264 | if content.startswith('tree\x00'): |
265 | if delta_tree_references is False: |
266 | raise InventoryDeltaError( |
267 | "Tree reference found (but header said " |
268 | - "tree_references: false): %r" % line) |
269 | + "tree_references: false): %(line)r", line=line) |
270 | elif not self._allow_tree_references: |
271 | raise IncompatibleInventoryDelta( |
272 | "Tree reference not allowed") |
273 | @@ -333,8 +347,8 @@ |
274 | oldpath = None |
275 | elif oldpath_utf8[:1] != '/': |
276 | raise InventoryDeltaError( |
277 | - "oldpath invalid (does not start with /): %r" |
278 | - % (oldpath_utf8,)) |
279 | + "oldpath invalid (does not start with /): %(path)r", |
280 | + path=oldpath_utf8) |
281 | else: |
282 | oldpath_utf8 = oldpath_utf8[1:] |
283 | oldpath = oldpath_utf8.decode('utf8') |
284 | @@ -342,8 +356,8 @@ |
285 | newpath = None |
286 | elif newpath_utf8[:1] != '/': |
287 | raise InventoryDeltaError( |
288 | - "newpath invalid (does not start with /): %r" |
289 | - % (newpath_utf8,)) |
290 | + "newpath invalid (does not start with /): %(path)r", |
291 | + path=newpath_utf8) |
292 | else: |
293 | # Trim leading slash |
294 | newpath_utf8 = newpath_utf8[1:] |
295 | |
296 | === modified file 'breezy/tests/test_errors.py' |
297 | --- breezy/tests/test_errors.py 2017-05-22 00:56:52 +0000 |
298 | +++ breezy/tests/test_errors.py 2017-06-09 16:32:43 +0000 |
299 | @@ -25,16 +25,15 @@ |
300 | controldir, |
301 | errors, |
302 | osutils, |
303 | + tests, |
304 | urlutils, |
305 | ) |
306 | -from . import ( |
307 | - TestCase, |
308 | - TestCaseWithTransport, |
309 | - TestSkipped, |
310 | +from ..sixish import ( |
311 | + text_type, |
312 | ) |
313 | |
314 | |
315 | -class TestErrors(TestCaseWithTransport): |
316 | +class TestErrors(tests.TestCase): |
317 | |
318 | def test_no_arg_named_message(self): |
319 | """Ensure the __init__ and _fmt in errors do not have "message" arg. |
320 | @@ -45,10 +44,6 @@ |
321 | See bug #603461 |
322 | """ |
323 | fmt_pattern = re.compile("%\(message\)[sir]") |
324 | - subclasses_present = getattr(errors.BzrError, '__subclasses__', None) |
325 | - if not subclasses_present: |
326 | - raise TestSkipped('__subclasses__ attribute required for classes. ' |
327 | - 'Requires Python 2.5 or later.') |
328 | for c in errors.BzrError.__subclasses__(): |
329 | init = getattr(c, '__init__', None) |
330 | fmt = getattr(c, '_fmt', None) |
331 | @@ -62,11 +57,11 @@ |
332 | '"errors.%s._fmt"' % c.__name__)) |
333 | |
334 | def test_bad_filename_encoding(self): |
335 | - error = errors.BadFilenameEncoding('bad/filen\xe5me', 'UTF-8') |
336 | - self.assertEqualDiff( |
337 | - "Filename 'bad/filen\\xe5me' is not valid in your current" |
338 | - " filesystem encoding UTF-8", |
339 | - str(error)) |
340 | + error = errors.BadFilenameEncoding(b'bad/filen\xe5me', 'UTF-8') |
341 | + self.assertContainsRe( |
342 | + str(error), |
343 | + "^Filename b?'bad/filen\\\\xe5me' is not valid in your current" |
344 | + " filesystem encoding UTF-8$") |
345 | |
346 | def test_corrupt_dirstate(self): |
347 | error = errors.CorruptDirstate('path/to/dirstate', 'the reason why') |
348 | @@ -199,19 +194,6 @@ |
349 | self.assertEqualDiff( |
350 | "The medium 'a medium' is not connected.", str(error)) |
351 | |
352 | - def test_no_public_branch(self): |
353 | - b = self.make_branch('.') |
354 | - error = errors.NoPublicBranch(b) |
355 | - url = urlutils.unescape_for_display(b.base, 'ascii') |
356 | - self.assertEqualDiff( |
357 | - 'There is no public branch set for "%s".' % url, str(error)) |
358 | - |
359 | - def test_no_repo(self): |
360 | - dir = controldir.ControlDir.create(self.get_url()) |
361 | - error = errors.NoRepositoryPresent(dir) |
362 | - self.assertNotEqual(-1, str(error).find((dir.transport.clone('..').base))) |
363 | - self.assertEqual(-1, str(error).find((dir.transport.base))) |
364 | - |
365 | def test_no_smart_medium(self): |
366 | error = errors.NoSmartMedium("a transport") |
367 | self.assertEqualDiff("The transport 'a transport' cannot tunnel the " |
368 | @@ -305,19 +287,11 @@ |
369 | "The branch format someformat is already at the most " |
370 | "recent format.", str(error)) |
371 | |
372 | - def test_corrupt_repository(self): |
373 | - repo = self.make_repository('.') |
374 | - error = errors.CorruptRepository(repo) |
375 | - self.assertEqualDiff("An error has been detected in the repository %s.\n" |
376 | - "Please run brz reconcile on this repository." % |
377 | - repo.bzrdir.root_transport.base, |
378 | - str(error)) |
379 | - |
380 | def test_read_error(self): |
381 | # a unicode path to check that %r is being used. |
382 | path = u'a path' |
383 | error = errors.ReadError(path) |
384 | - self.assertEqualDiff("Error reading from u'a path'.", str(error)) |
385 | + self.assertContainsRe(str(error), "^Error reading from u?'a path'.$") |
386 | |
387 | def test_bad_index_format_signature(self): |
388 | error = errors.BadIndexFormatSignature("foo", "bar") |
389 | @@ -445,14 +419,6 @@ |
390 | 'See "brz help bugs" for more information on this feature.', |
391 | str(error)) |
392 | |
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) |
397 | - self.assertEqual( |
398 | - "Cannot find registered bug tracker called xxx on %s" % branch, |
399 | - str(error)) |
400 | - |
401 | def test_unexpected_smart_server_response(self): |
402 | e = errors.UnexpectedSmartServerResponse(('not yes',)) |
403 | self.assertEqual( |
404 | @@ -495,14 +461,12 @@ |
405 | |
406 | def test_duplicate_record_name_error(self): |
407 | """Test the formatting of DuplicateRecordNameError.""" |
408 | - e = errors.DuplicateRecordNameError(u"n\xe5me".encode('utf-8')) |
409 | + e = errors.DuplicateRecordNameError(b"n\xc3\xa5me") |
410 | self.assertEqual( |
411 | - "Container has multiple records with the same name: n\xc3\xa5me", |
412 | - str(e)) |
413 | + u"Container has multiple records with the same name: n\xe5me", |
414 | + text_type(e)) |
415 | |
416 | def test_check_error(self): |
417 | - # This has a member called 'message', which is problematic in |
418 | - # python2.5 because that is a slot on the base Exception class |
419 | e = errors.BzrCheckError('example check failure') |
420 | self.assertEqual( |
421 | "Internal check failed: example check failure", |
422 | @@ -534,7 +498,7 @@ |
423 | str(err)) |
424 | err = errors.UnableCreateSymlink(path=u'\xb5') |
425 | self.assertEqual( |
426 | - "Unable to create symlink u'\\xb5' on this platform", |
427 | + "Unable to create symlink %s on this platform" % repr(u'\xb5'), |
428 | str(err)) |
429 | |
430 | def test_invalid_url_join(self): |
431 | @@ -569,10 +533,7 @@ |
432 | err = errors.TipChangeRejected(u'Unicode message\N{INTERROBANG}') |
433 | self.assertEqual( |
434 | u'Tip change rejected: Unicode message\N{INTERROBANG}', |
435 | - unicode(err)) |
436 | - self.assertEqual( |
437 | - 'Tip change rejected: Unicode message\xe2\x80\xbd', |
438 | - str(err)) |
439 | + text_type(err)) |
440 | |
441 | def test_error_from_smart_server(self): |
442 | error_tuple = ('error', 'tuple') |
443 | @@ -647,17 +608,6 @@ |
444 | err = errors.NotBranchError('path') |
445 | self.assertEqual('Not a branch: "path".', str(err)) |
446 | |
447 | - def test_not_branch_bzrdir_with_repo(self): |
448 | - bzrdir = self.make_repository('repo').bzrdir |
449 | - err = errors.NotBranchError('path', bzrdir=bzrdir) |
450 | - self.assertEqual( |
451 | - 'Not a branch: "path": location is a repository.', str(err)) |
452 | - |
453 | - def test_not_branch_bzrdir_without_repo(self): |
454 | - bzrdir = self.make_bzrdir('bzrdir') |
455 | - err = errors.NotBranchError('path', bzrdir=bzrdir) |
456 | - self.assertEqual('Not a branch: "path".', str(err)) |
457 | - |
458 | def test_not_branch_bzrdir_with_recursive_not_branch_error(self): |
459 | class FakeBzrDir(object): |
460 | def open_repository(self): |
461 | @@ -665,24 +615,7 @@ |
462 | # which in turn will another, identical NotBranchError. |
463 | raise errors.NotBranchError('path', bzrdir=FakeBzrDir()) |
464 | err = errors.NotBranchError('path', bzrdir=FakeBzrDir()) |
465 | - self.assertEqual('Not a branch: "path".', str(err)) |
466 | - |
467 | - def test_not_branch_laziness(self): |
468 | - real_bzrdir = self.make_bzrdir('path') |
469 | - class FakeBzrDir(object): |
470 | - def __init__(self): |
471 | - self.calls = [] |
472 | - def open_repository(self): |
473 | - self.calls.append('open_repository') |
474 | - raise errors.NoRepositoryPresent(real_bzrdir) |
475 | - fake_bzrdir = FakeBzrDir() |
476 | - err = errors.NotBranchError('path', bzrdir=fake_bzrdir) |
477 | - self.assertEqual([], fake_bzrdir.calls) |
478 | - str(err) |
479 | - self.assertEqual(['open_repository'], fake_bzrdir.calls) |
480 | - # Stringifying twice doesn't try to open a repository twice. |
481 | - str(err) |
482 | - self.assertEqual(['open_repository'], fake_bzrdir.calls) |
483 | + self.assertEqual('Not a branch: "path": NotBranchError.', str(err)) |
484 | |
485 | def test_invalid_pattern(self): |
486 | error = errors.InvalidPattern('Bad pattern msg.') |
487 | @@ -721,7 +654,7 @@ |
488 | __doc__ = """This class has a docstring but no format string.""" |
489 | |
490 | |
491 | -class TestErrorFormatting(TestCase): |
492 | +class TestErrorFormatting(tests.TestCase): |
493 | |
494 | def test_always_str(self): |
495 | e = PassThroughError(u'\xb5', 'bar') |
496 | @@ -764,3 +697,64 @@ |
497 | self.assertEqual( |
498 | u"Failed to rename from to to: readonly file", |
499 | str(e)) |
500 | + |
501 | + |
502 | +class TestErrorsUsingTransport(tests.TestCaseWithMemoryTransport): |
503 | + """Tests for errors that need to use a branch or repo.""" |
504 | + |
505 | + def test_no_public_branch(self): |
506 | + b = self.make_branch('.') |
507 | + error = errors.NoPublicBranch(b) |
508 | + url = urlutils.unescape_for_display(b.base, 'ascii') |
509 | + self.assertEqualDiff( |
510 | + 'There is no public branch set for "%s".' % url, str(error)) |
511 | + |
512 | + def test_no_repo(self): |
513 | + dir = controldir.ControlDir.create(self.get_url()) |
514 | + error = errors.NoRepositoryPresent(dir) |
515 | + self.assertNotEqual(-1, str(error).find((dir.transport.clone('..').base))) |
516 | + self.assertEqual(-1, str(error).find((dir.transport.base))) |
517 | + |
518 | + def test_corrupt_repository(self): |
519 | + repo = self.make_repository('.') |
520 | + error = errors.CorruptRepository(repo) |
521 | + self.assertEqualDiff("An error has been detected in the repository %s.\n" |
522 | + "Please run brz reconcile on this repository." % |
523 | + repo.bzrdir.root_transport.base, |
524 | + str(error)) |
525 | + |
526 | + def test_unknown_bug_tracker_abbreviation(self): |
527 | + """Test the formatting of UnknownBugTrackerAbbreviation.""" |
528 | + branch = self.make_branch('some_branch') |
529 | + error = errors.UnknownBugTrackerAbbreviation('xxx', branch) |
530 | + self.assertEqual( |
531 | + "Cannot find registered bug tracker called xxx on %s" % branch, |
532 | + str(error)) |
533 | + |
534 | + def test_not_branch_bzrdir_with_repo(self): |
535 | + bzrdir = self.make_repository('repo').bzrdir |
536 | + err = errors.NotBranchError('path', bzrdir=bzrdir) |
537 | + self.assertEqual( |
538 | + 'Not a branch: "path": location is a repository.', str(err)) |
539 | + |
540 | + def test_not_branch_bzrdir_without_repo(self): |
541 | + bzrdir = self.make_bzrdir('bzrdir') |
542 | + err = errors.NotBranchError('path', bzrdir=bzrdir) |
543 | + self.assertEqual('Not a branch: "path".', str(err)) |
544 | + |
545 | + def test_not_branch_laziness(self): |
546 | + real_bzrdir = self.make_bzrdir('path') |
547 | + class FakeBzrDir(object): |
548 | + def __init__(self): |
549 | + self.calls = [] |
550 | + def open_repository(self): |
551 | + self.calls.append('open_repository') |
552 | + raise errors.NoRepositoryPresent(real_bzrdir) |
553 | + fake_bzrdir = FakeBzrDir() |
554 | + err = errors.NotBranchError('path', bzrdir=fake_bzrdir) |
555 | + self.assertEqual([], fake_bzrdir.calls) |
556 | + str(err) |
557 | + self.assertEqual(['open_repository'], fake_bzrdir.calls) |
558 | + # Stringifying twice doesn't try to open a repository twice. |
559 | + str(err) |
560 | + self.assertEqual(['open_repository'], fake_bzrdir.calls) |
561 | |
562 | === modified file 'breezy/tests/test_inventory_delta.py' |
563 | --- breezy/tests/test_inventory_delta.py 2017-05-22 00:56:52 +0000 |
564 | +++ breezy/tests/test_inventory_delta.py 2017-06-09 16:32:43 +0000 |
565 | @@ -354,7 +354,7 @@ |
566 | versioned_root=True, tree_references=True) |
567 | err = self.assertRaises(InventoryDeltaError, |
568 | serializer.delta_to_lines, NULL_REVISION, 'entry-version', delta) |
569 | - self.assertEqual(str(err), 'no version for fileid id') |
570 | + self.assertContainsRe(str(err), "^no version for fileid b?'id'$") |
571 | |
572 | def test_richroot_unversioned_root_errors(self): |
573 | old_inv = Inventory(None) |
574 | @@ -366,7 +366,8 @@ |
575 | versioned_root=True, tree_references=True) |
576 | err = self.assertRaises(InventoryDeltaError, |
577 | serializer.delta_to_lines, NULL_REVISION, 'entry-version', delta) |
578 | - self.assertEqual(str(err), 'no version for fileid TREE_ROOT') |
579 | + self.assertContainsRe( |
580 | + str(err), "no version for fileid b?'TREE_ROOT'$") |
581 | |
582 | def test_nonrichroot_versioned_root_errors(self): |
583 | old_inv = Inventory(None) |
584 | @@ -379,7 +380,8 @@ |
585 | versioned_root=False, tree_references=True) |
586 | err = self.assertRaises(InventoryDeltaError, |
587 | serializer.delta_to_lines, NULL_REVISION, 'entry-version', delta) |
588 | - self.assertStartsWith(str(err), 'Version present for / in TREE_ROOT') |
589 | + self.assertContainsRe( |
590 | + str(err), "^Version present for / in b?'TREE_ROOT'") |
591 | |
592 | def test_unknown_kind_errors(self): |
593 | old_inv = Inventory(None) |
Running landing tests failed 10.242. 247.184: 8080/job/ brz-dev/ 97/
http://