Merge lp:~jelmer/ubuntu/maverick/bzr/2.2.5 into lp:ubuntu/maverick-updates/bzr
- Maverick (10.10)
- 2.2.5
- Merge into maverick-updates
Proposed by
Jelmer Vernooij
Status: | Merged | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Merge reported by: | Colin Watson | ||||||||||||
Merged at revision: | not available | ||||||||||||
Proposed branch: | lp:~jelmer/ubuntu/maverick/bzr/2.2.5 | ||||||||||||
Merge into: | lp:ubuntu/maverick-updates/bzr | ||||||||||||
Diff against target: |
1694 lines (+1330/-32) 24 files modified
Makefile (+1/-1) NEWS (+107/-1) bzr (+2/-2) bzrlib/__init__.py (+3/-3) bzrlib/groupcompress.py (+2/-2) bzrlib/help_topics/en/configuration.txt (+3/-1) bzrlib/knit.py (+1/-1) bzrlib/merge.py (+8/-2) bzrlib/plugins/launchpad/__init__.py (+64/-4) bzrlib/plugins/launchpad/lp_api_lite.py (+285/-0) bzrlib/plugins/launchpad/test_lp_api_lite.py (+557/-0) bzrlib/repofmt/groupcompress_repo.py (+13/-4) bzrlib/tests/per_repository_reference/__init__.py (+1/-0) bzrlib/tests/per_repository_reference/test_graph.py (+45/-0) bzrlib/tests/test_config.py (+27/-1) bzrlib/tests/test_conflicts.py (+64/-0) bzrlib/tests/test_repository.py (+101/-1) bzrlib/transport/http/_pycurl.py (+5/-5) bzrlib/util/configobj/configobj.py (+4/-2) bzrlib/versionedfile.py (+13/-0) debian/changelog (+10/-0) debian/watch (+1/-1) doc/en/whats-new/whats-new-in-2.2.txt (+4/-1) setup.py (+9/-0) |
||||||||||||
To merge this branch: | bzr merge lp:~jelmer/ubuntu/maverick/bzr/2.2.5 | ||||||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Colin Watson | Approve | ||
Ubuntu Sponsors | Pending | ||
Review via email: mp+77153@code.launchpad.net |
This proposal supersedes a proposal from 2011-09-27.
Commit message
Description of the change
bzr microrelease 2.2.5
This fixes a couple of bugs that might be affecting users:
* stacking is not fully transitive (715000)
* merge failing with NoFinalPath (#805809)
To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'Makefile' | |||
2 | --- Makefile 2010-08-07 00:54:52 +0000 | |||
3 | +++ Makefile 2011-09-27 11:59:22 +0000 | |||
4 | @@ -40,7 +40,7 @@ | |||
5 | 40 | 40 | ||
6 | 41 | check-nodocs: extensions | 41 | check-nodocs: extensions |
7 | 42 | # Generate a stream for PQM to watch. | 42 | # Generate a stream for PQM to watch. |
9 | 43 | $(PYTHON) -Werror -O ./bzr selftest --subunit $(tests) | tee selftest.log | 43 | $(PYTHON) -Werror -Wignore::ImportWarning -O ./bzr selftest --subunit $(tests) | tee selftest.log |
10 | 44 | # Check that there were no errors reported. | 44 | # Check that there were no errors reported. |
11 | 45 | subunit-stats < selftest.log | 45 | subunit-stats < selftest.log |
12 | 46 | 46 | ||
13 | 47 | 47 | ||
14 | === modified file 'NEWS' | |||
15 | --- NEWS 2011-04-07 15:30:17 +0000 | |||
16 | +++ NEWS 2011-09-27 11:59:22 +0000 | |||
17 | @@ -5,6 +5,98 @@ | |||
18 | 5 | .. contents:: List of Releases | 5 | .. contents:: List of Releases |
19 | 6 | :depth: 1 | 6 | :depth: 1 |
20 | 7 | 7 | ||
21 | 8 | This is a bugfix release. One regression introduced in 2.2b1 has been fixed | ||
22 | 9 | for some rare conflict resolutions. Also a warning is now emmitted when | ||
23 | 10 | branching an out-of-date ubuntu packaging branch. Upgrading is recommended | ||
24 | 11 | for all users on earlier 2.2 releases. | ||
25 | 12 | |||
26 | 13 | bzr 2.2.5 | ||
27 | 14 | ######### | ||
28 | 15 | |||
29 | 16 | :2.2.5: 2011-09-01 | ||
30 | 17 | |||
31 | 18 | Compatibility Breaks | ||
32 | 19 | ******************** | ||
33 | 20 | |||
34 | 21 | None. | ||
35 | 22 | |||
36 | 23 | New Features | ||
37 | 24 | ************ | ||
38 | 25 | |||
39 | 26 | None. | ||
40 | 27 | |||
41 | 28 | Bug Fixes | ||
42 | 29 | ********* | ||
43 | 30 | |||
44 | 31 | * Correctly handle ``bzr log`` and `get_known_graph_ancestry` on a | ||
45 | 32 | doubly-stacked branch. | ||
46 | 33 | (James Westby, Martin Pool, #715000) | ||
47 | 34 | |||
48 | 35 | * Don't crash while merging and encountering obscure path conflicts | ||
49 | 36 | involving different root-ids. (Vincent Ladeuil, #805809) | ||
50 | 37 | |||
51 | 38 | Internals | ||
52 | 39 | ********* | ||
53 | 40 | |||
54 | 41 | * Fixed bug in the bundled copy of ConfigObj with quoting of triple quotes | ||
55 | 42 | in the value string. Fix suggested by ConfigObj's author Michael Foord. | ||
56 | 43 | (Alexander Belchenko, #710410) | ||
57 | 44 | |||
58 | 45 | bzr 2.1.5 | ||
59 | 46 | ######### | ||
60 | 47 | |||
61 | 48 | :2.1.5: NOT RELEASED YET | ||
62 | 49 | |||
63 | 50 | Compatibility Breaks | ||
64 | 51 | ******************** | ||
65 | 52 | |||
66 | 53 | New Features | ||
67 | 54 | ************ | ||
68 | 55 | |||
69 | 56 | Bug Fixes | ||
70 | 57 | ********* | ||
71 | 58 | |||
72 | 59 | * Accessing a packaging branch on Launchpad (eg, ``lp:ubuntu/bzr``) now | ||
73 | 60 | checks to see if the most recent published source package version for | ||
74 | 61 | that project is present in the branch tags. This should help developers | ||
75 | 62 | trust whether the packaging branch is up-to-date and can be used for new | ||
76 | 63 | changes. The level of verbosity is controlled by the config item | ||
77 | 64 | ``launchpad.packaging_verbosity``. It can be set to one of | ||
78 | 65 | |||
79 | 66 | off | ||
80 | 67 | disable all checks | ||
81 | 68 | |||
82 | 69 | |||
83 | 70 | minimal | ||
84 | 71 | only display if the branch is out-of-date | ||
85 | 72 | |||
86 | 73 | short | ||
87 | 74 | also display single-line up-to-date and missing, | ||
88 | 75 | |||
89 | 76 | |||
90 | 77 | all | ||
91 | 78 | (default) display multi-line content for all states | ||
92 | 79 | |||
93 | 80 | |||
94 | 81 | (John Arbash Meinel, #609187, #812928) | ||
95 | 82 | |||
96 | 83 | |||
97 | 84 | Improvements | ||
98 | 85 | ************ | ||
99 | 86 | |||
100 | 87 | Documentation | ||
101 | 88 | ************* | ||
102 | 89 | |||
103 | 90 | API Changes | ||
104 | 91 | *********** | ||
105 | 92 | |||
106 | 93 | Internals | ||
107 | 94 | ********* | ||
108 | 95 | |||
109 | 96 | Testing | ||
110 | 97 | ******* | ||
111 | 98 | |||
112 | 99 | |||
113 | 8 | bzr 2.2.4 | 100 | bzr 2.2.4 |
114 | 9 | ######### | 101 | ######### |
115 | 10 | 102 | ||
116 | @@ -251,7 +343,12 @@ | |||
117 | 251 | bzr 2.1.4 | 343 | bzr 2.1.4 |
118 | 252 | ######### | 344 | ######### |
119 | 253 | 345 | ||
121 | 254 | :2.1.4: NOT RELEASED YET | 346 | :2.1.4: 2011-05-16 |
122 | 347 | |||
123 | 348 | The fourth release in our 2.1 series addresses some user-inconvenience bugs. | ||
124 | 349 | None are critical, but upgrading is recommended for all users on earlier 2.1 | ||
125 | 350 | releases. | ||
126 | 351 | |||
127 | 255 | 352 | ||
128 | 256 | Compatibility Breaks | 353 | Compatibility Breaks |
129 | 257 | ******************** | 354 | ******************** |
130 | @@ -265,6 +362,8 @@ | |||
131 | 265 | New Features | 362 | New Features |
132 | 266 | ************ | 363 | ************ |
133 | 267 | 364 | ||
134 | 365 | None. | ||
135 | 366 | |||
136 | 268 | Bug Fixes | 367 | Bug Fixes |
137 | 269 | ********* | 368 | ********* |
138 | 270 | 369 | ||
139 | @@ -403,6 +502,13 @@ | |||
140 | 403 | * Avoid UnicodeDecodeError in ``bzr add`` with multiple files under a non-ascii | 502 | * Avoid UnicodeDecodeError in ``bzr add`` with multiple files under a non-ascii |
141 | 404 | path on windows from symlink support addition. (Martin [gz], #686611) | 503 | path on windows from symlink support addition. (Martin [gz], #686611) |
142 | 405 | 504 | ||
143 | 505 | * Avoid spurious ValueErrors when autopacking a subset of the repository, | ||
144 | 506 | and seeing a revision without its related inventory | ||
145 | 507 | (John Arbash Meinel, #437003) | ||
146 | 508 | |||
147 | 509 | * Fix activity reporting for pycurl when using https with some | ||
148 | 510 | implementations of curl. (Vincent Ladeuil, #614713) | ||
149 | 511 | |||
150 | 406 | Improvements | 512 | Improvements |
151 | 407 | ************ | 513 | ************ |
152 | 408 | 514 | ||
153 | 409 | 515 | ||
154 | === modified file 'bzr' | |||
155 | --- bzr 2011-04-07 15:30:17 +0000 | |||
156 | +++ bzr 2011-09-27 11:59:22 +0000 | |||
157 | @@ -1,6 +1,6 @@ | |||
158 | 1 | #! /usr/bin/env python | 1 | #! /usr/bin/env python |
159 | 2 | 2 | ||
161 | 3 | # Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Canonical Ltd | 3 | # Copyright (C) 2005-2011 Canonical Ltd |
162 | 4 | # | 4 | # |
163 | 5 | # This program is free software; you can redistribute it and/or modify | 5 | # This program is free software; you can redistribute it and/or modify |
164 | 6 | # it under the terms of the GNU General Public License as published by | 6 | # it under the terms of the GNU General Public License as published by |
165 | @@ -23,7 +23,7 @@ | |||
166 | 23 | import warnings | 23 | import warnings |
167 | 24 | 24 | ||
168 | 25 | # update this on each release | 25 | # update this on each release |
170 | 26 | _script_version = (2, 2, 4) | 26 | _script_version = (2, 2, 5) |
171 | 27 | 27 | ||
172 | 28 | try: | 28 | try: |
173 | 29 | version_info = sys.version_info | 29 | version_info = sys.version_info |
174 | 30 | 30 | ||
175 | === modified file 'bzrlib/__init__.py' | |||
176 | --- bzrlib/__init__.py 2011-04-07 15:30:17 +0000 | |||
177 | +++ bzrlib/__init__.py 2011-09-27 11:59:22 +0000 | |||
178 | @@ -1,4 +1,4 @@ | |||
180 | 1 | # Copyright (C) 2005-2010 Canonical Ltd | 1 | # Copyright (C) 2005-2011 Canonical Ltd |
181 | 2 | # | 2 | # |
182 | 3 | # This program is free software; you can redistribute it and/or modify | 3 | # This program is free software; you can redistribute it and/or modify |
183 | 4 | # it under the terms of the GNU General Public License as published by | 4 | # it under the terms of the GNU General Public License as published by |
184 | @@ -43,7 +43,7 @@ | |||
185 | 43 | IGNORE_FILENAME = ".bzrignore" | 43 | IGNORE_FILENAME = ".bzrignore" |
186 | 44 | 44 | ||
187 | 45 | 45 | ||
189 | 46 | __copyright__ = "Copyright 2005-2010 Canonical Ltd." | 46 | __copyright__ = "Copyright 2005-2011 Canonical Ltd." |
190 | 47 | 47 | ||
191 | 48 | # same format as sys.version_info: "A tuple containing the five components of | 48 | # same format as sys.version_info: "A tuple containing the five components of |
192 | 49 | # the version number: major, minor, micro, releaselevel, and serial. All | 49 | # the version number: major, minor, micro, releaselevel, and serial. All |
193 | @@ -52,7 +52,7 @@ | |||
194 | 52 | # Python version 2.0 is (2, 0, 0, 'final', 0)." Additionally we use a | 52 | # Python version 2.0 is (2, 0, 0, 'final', 0)." Additionally we use a |
195 | 53 | # releaselevel of 'dev' for unreleased under-development code. | 53 | # releaselevel of 'dev' for unreleased under-development code. |
196 | 54 | 54 | ||
198 | 55 | version_info = (2, 2, 4, 'final', 0) | 55 | version_info = (2, 2, 5, 'final', 0) |
199 | 56 | 56 | ||
200 | 57 | # API compatibility version | 57 | # API compatibility version |
201 | 58 | api_minimum_version = (2, 2, 0) | 58 | api_minimum_version = (2, 2, 0) |
202 | 59 | 59 | ||
203 | === modified file 'bzrlib/groupcompress.py' | |||
204 | --- bzrlib/groupcompress.py 2010-05-27 21:58:49 +0000 | |||
205 | +++ bzrlib/groupcompress.py 2011-09-27 11:59:22 +0000 | |||
206 | @@ -1,4 +1,4 @@ | |||
208 | 1 | # Copyright (C) 2008, 2009, 2010 Canonical Ltd | 1 | # Copyright (C) 2008-2011 Canonical Ltd |
209 | 2 | # | 2 | # |
210 | 3 | # This program is free software; you can redistribute it and/or modify | 3 | # This program is free software; you can redistribute it and/or modify |
211 | 4 | # it under the terms of the GNU General Public License as published by | 4 | # it under the terms of the GNU General Public License as published by |
212 | @@ -1293,7 +1293,7 @@ | |||
213 | 1293 | # KnitVersionedFiles.get_known_graph_ancestry, but they don't share | 1293 | # KnitVersionedFiles.get_known_graph_ancestry, but they don't share |
214 | 1294 | # ancestry. | 1294 | # ancestry. |
215 | 1295 | parent_map, missing_keys = self._index.find_ancestry(keys) | 1295 | parent_map, missing_keys = self._index.find_ancestry(keys) |
217 | 1296 | for fallback in self._fallback_vfs: | 1296 | for fallback in self._transitive_fallbacks(): |
218 | 1297 | if not missing_keys: | 1297 | if not missing_keys: |
219 | 1298 | break | 1298 | break |
220 | 1299 | (f_parent_map, f_missing_keys) = fallback._index.find_ancestry( | 1299 | (f_parent_map, f_missing_keys) = fallback._index.find_ancestry( |
221 | 1300 | 1300 | ||
222 | === modified file 'bzrlib/help_topics/en/configuration.txt' | |||
223 | --- bzrlib/help_topics/en/configuration.txt 2010-08-07 00:54:52 +0000 | |||
224 | +++ bzrlib/help_topics/en/configuration.txt 2011-09-27 11:59:22 +0000 | |||
225 | @@ -19,7 +19,9 @@ | |||
226 | 19 | BZR_PROGRESS_BAR | 19 | BZR_PROGRESS_BAR |
227 | 20 | ~~~~~~~~~~~~~~~~ | 20 | ~~~~~~~~~~~~~~~~ |
228 | 21 | 21 | ||
230 | 22 | Override the progress display. Possible values are "none", "dots", "tty" | 22 | Override the progress display. Possible values are "none" or "text". If |
231 | 23 | the value is "none" then no progress bar is displayed. The value "text" draws | ||
232 | 24 | the ordinary command line progress bar. | ||
233 | 23 | 25 | ||
234 | 24 | BZR_SIGQUIT_PDB | 26 | BZR_SIGQUIT_PDB |
235 | 25 | ~~~~~~~~~~~~~~~ | 27 | ~~~~~~~~~~~~~~~ |
236 | 26 | 28 | ||
237 | === modified file 'bzrlib/knit.py' | |||
238 | --- bzrlib/knit.py 2010-08-07 00:54:52 +0000 | |||
239 | +++ bzrlib/knit.py 2011-09-27 11:59:22 +0000 | |||
240 | @@ -1195,7 +1195,7 @@ | |||
241 | 1195 | def get_known_graph_ancestry(self, keys): | 1195 | def get_known_graph_ancestry(self, keys): |
242 | 1196 | """Get a KnownGraph instance with the ancestry of keys.""" | 1196 | """Get a KnownGraph instance with the ancestry of keys.""" |
243 | 1197 | parent_map, missing_keys = self._index.find_ancestry(keys) | 1197 | parent_map, missing_keys = self._index.find_ancestry(keys) |
245 | 1198 | for fallback in self._fallback_vfs: | 1198 | for fallback in self._transitive_fallbacks(): |
246 | 1199 | if not missing_keys: | 1199 | if not missing_keys: |
247 | 1200 | break | 1200 | break |
248 | 1201 | (f_parent_map, f_missing_keys) = fallback._index.find_ancestry( | 1201 | (f_parent_map, f_missing_keys) = fallback._index.find_ancestry( |
249 | 1202 | 1202 | ||
250 | === modified file 'bzrlib/merge.py' | |||
251 | --- bzrlib/merge.py 2010-08-07 00:54:52 +0000 | |||
252 | +++ bzrlib/merge.py 2011-09-27 11:59:22 +0000 | |||
253 | @@ -1624,8 +1624,14 @@ | |||
254 | 1624 | if other_parent is None or other_name is None: | 1624 | if other_parent is None or other_name is None: |
255 | 1625 | other_path = '<deleted>' | 1625 | other_path = '<deleted>' |
256 | 1626 | else: | 1626 | else: |
259 | 1627 | parent_path = fp.get_path( | 1627 | if other_parent == self.other_tree.get_root_id(): |
260 | 1628 | self.tt.trans_id_file_id(other_parent)) | 1628 | # The tree transform doesn't know about the other root, |
261 | 1629 | # so we special case here to avoid a NoFinalPath | ||
262 | 1630 | # exception | ||
263 | 1631 | parent_path = '' | ||
264 | 1632 | else: | ||
265 | 1633 | parent_path = fp.get_path( | ||
266 | 1634 | self.tt.trans_id_file_id(other_parent)) | ||
267 | 1629 | other_path = osutils.pathjoin(parent_path, other_name) | 1635 | other_path = osutils.pathjoin(parent_path, other_name) |
268 | 1630 | c = _mod_conflicts.Conflict.factory( | 1636 | c = _mod_conflicts.Conflict.factory( |
269 | 1631 | 'path conflict', path=this_path, | 1637 | 'path conflict', path=this_path, |
270 | 1632 | 1638 | ||
271 | === modified file 'bzrlib/plugins/launchpad/__init__.py' | |||
272 | --- bzrlib/plugins/launchpad/__init__.py 2010-08-07 00:54:52 +0000 | |||
273 | +++ bzrlib/plugins/launchpad/__init__.py 2011-09-27 11:59:22 +0000 | |||
274 | @@ -21,13 +21,9 @@ | |||
275 | 21 | 21 | ||
276 | 22 | # see http://bazaar-vcs.org/Specs/BranchRegistrationTool | 22 | # see http://bazaar-vcs.org/Specs/BranchRegistrationTool |
277 | 23 | 23 | ||
278 | 24 | # Since we are a built-in plugin we share the bzrlib version | ||
279 | 25 | from bzrlib import version_info | ||
280 | 26 | |||
281 | 27 | from bzrlib.lazy_import import lazy_import | 24 | from bzrlib.lazy_import import lazy_import |
282 | 28 | lazy_import(globals(), """ | 25 | lazy_import(globals(), """ |
283 | 29 | from bzrlib import ( | 26 | from bzrlib import ( |
284 | 30 | branch as _mod_branch, | ||
285 | 31 | trace, | 27 | trace, |
286 | 32 | ) | 28 | ) |
287 | 33 | """) | 29 | """) |
288 | @@ -37,6 +33,12 @@ | |||
289 | 37 | Command, | 33 | Command, |
290 | 38 | register_command, | 34 | register_command, |
291 | 39 | ) | 35 | ) |
292 | 36 | from bzrlib import ( | ||
293 | 37 | branch as _mod_branch, | ||
294 | 38 | lazy_regex, | ||
295 | 39 | # Since we are a built-in plugin we share the bzrlib version | ||
296 | 40 | version_info, | ||
297 | 41 | ) | ||
298 | 40 | from bzrlib.directory_service import directories | 42 | from bzrlib.directory_service import directories |
299 | 41 | from bzrlib.errors import ( | 43 | from bzrlib.errors import ( |
300 | 42 | BzrCommandError, | 44 | BzrCommandError, |
301 | @@ -354,12 +356,70 @@ | |||
302 | 354 | 'Launchpad-based directory service',) | 356 | 'Launchpad-based directory service',) |
303 | 355 | _register_directory() | 357 | _register_directory() |
304 | 356 | 358 | ||
305 | 359 | # This is kept in __init__ so that we don't load lp_api_lite unless the branch | ||
306 | 360 | # actually matches. That way we can avoid importing extra dependencies like | ||
307 | 361 | # json. | ||
308 | 362 | _package_branch = lazy_regex.lazy_compile( | ||
309 | 363 | r'bazaar.launchpad.net.*?/' | ||
310 | 364 | r'(?P<user>~[^/]+/)?(?P<archive>ubuntu|debian)/(?P<series>[^/]+/)?' | ||
311 | 365 | r'(?P<project>[^/]+)(?P<branch>/[^/]+)?' | ||
312 | 366 | ) | ||
313 | 367 | |||
314 | 368 | def _get_package_branch_info(url): | ||
315 | 369 | """Determine the packaging information for this URL. | ||
316 | 370 | |||
317 | 371 | :return: If this isn't a packaging branch, return None. If it is, return | ||
318 | 372 | (archive, series, project) | ||
319 | 373 | """ | ||
320 | 374 | if url is None: | ||
321 | 375 | return None | ||
322 | 376 | m = _package_branch.search(url) | ||
323 | 377 | if m is None: | ||
324 | 378 | return None | ||
325 | 379 | archive, series, project, user = m.group('archive', 'series', | ||
326 | 380 | 'project', 'user') | ||
327 | 381 | if series is not None: | ||
328 | 382 | # series is optional, so the regex includes the extra '/', we don't | ||
329 | 383 | # want to send that on (it causes Internal Server Errors.) | ||
330 | 384 | series = series.strip('/') | ||
331 | 385 | if user is not None: | ||
332 | 386 | user = user.strip('~/') | ||
333 | 387 | if user != 'ubuntu-branches': | ||
334 | 388 | return None | ||
335 | 389 | return archive, series, project | ||
336 | 390 | |||
337 | 391 | |||
338 | 392 | def _check_is_up_to_date(the_branch): | ||
339 | 393 | info = _get_package_branch_info(the_branch.base) | ||
340 | 394 | if info is None: | ||
341 | 395 | return | ||
342 | 396 | c = the_branch.get_config() | ||
343 | 397 | verbosity = c.get_user_option('launchpad.packaging_verbosity') | ||
344 | 398 | if verbosity is not None: | ||
345 | 399 | verbosity = verbosity.lower() | ||
346 | 400 | if verbosity == 'off': | ||
347 | 401 | trace.mutter('not checking %s because verbosity is turned off' | ||
348 | 402 | % (the_branch.base,)) | ||
349 | 403 | return | ||
350 | 404 | archive, series, project = info | ||
351 | 405 | from bzrlib.plugins.launchpad import lp_api_lite | ||
352 | 406 | latest_pub = lp_api_lite.LatestPublication(archive, series, project) | ||
353 | 407 | lp_api_lite.report_freshness(the_branch, verbosity, latest_pub) | ||
354 | 408 | |||
355 | 409 | |||
356 | 410 | def _register_hooks(): | ||
357 | 411 | _mod_branch.Branch.hooks.install_named_hook('open', | ||
358 | 412 | _check_is_up_to_date, 'package-branch-up-to-date') | ||
359 | 413 | |||
360 | 414 | |||
361 | 415 | _register_hooks() | ||
362 | 357 | 416 | ||
363 | 358 | def load_tests(basic_tests, module, loader): | 417 | def load_tests(basic_tests, module, loader): |
364 | 359 | testmod_names = [ | 418 | testmod_names = [ |
365 | 360 | 'test_account', | 419 | 'test_account', |
366 | 361 | 'test_register', | 420 | 'test_register', |
367 | 362 | 'test_lp_api', | 421 | 'test_lp_api', |
368 | 422 | 'test_lp_api_lite', | ||
369 | 363 | 'test_lp_directory', | 423 | 'test_lp_directory', |
370 | 364 | 'test_lp_login', | 424 | 'test_lp_login', |
371 | 365 | 'test_lp_open', | 425 | 'test_lp_open', |
372 | 366 | 426 | ||
373 | === added file 'bzrlib/plugins/launchpad/lp_api_lite.py' | |||
374 | --- bzrlib/plugins/launchpad/lp_api_lite.py 1970-01-01 00:00:00 +0000 | |||
375 | +++ bzrlib/plugins/launchpad/lp_api_lite.py 2011-09-27 11:59:22 +0000 | |||
376 | @@ -0,0 +1,285 @@ | |||
377 | 1 | # Copyright (C) 2011 Canonical Ltd | ||
378 | 2 | # | ||
379 | 3 | # This program is free software; you can redistribute it and/or modify | ||
380 | 4 | # it under the terms of the GNU General Public License as published by | ||
381 | 5 | # the Free Software Foundation; either version 2 of the License, or | ||
382 | 6 | # (at your option) any later version. | ||
383 | 7 | # | ||
384 | 8 | # This program is distributed in the hope that it will be useful, | ||
385 | 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
386 | 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
387 | 11 | # GNU General Public License for more details. | ||
388 | 12 | # | ||
389 | 13 | # You should have received a copy of the GNU General Public License | ||
390 | 14 | # along with this program; if not, write to the Free Software | ||
391 | 15 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
392 | 16 | |||
393 | 17 | """Tools for dealing with the Launchpad API without using launchpadlib. | ||
394 | 18 | |||
395 | 19 | The api itself is a RESTful interface, so we can make HTTP queries directly. | ||
396 | 20 | loading launchpadlib itself has a fairly high overhead (just calling | ||
397 | 21 | Launchpad.login_anonymously() takes a 500ms once the WADL is cached, and 5+s to | ||
398 | 22 | get the WADL. | ||
399 | 23 | """ | ||
400 | 24 | |||
401 | 25 | try: | ||
402 | 26 | # Use simplejson if available, much faster, and can be easily installed in | ||
403 | 27 | # older versions of python | ||
404 | 28 | import simplejson as json | ||
405 | 29 | except ImportError: | ||
406 | 30 | # Is present since python 2.6 | ||
407 | 31 | try: | ||
408 | 32 | import json | ||
409 | 33 | except ImportError: | ||
410 | 34 | json = None | ||
411 | 35 | |||
412 | 36 | import time | ||
413 | 37 | import urllib | ||
414 | 38 | import urllib2 | ||
415 | 39 | |||
416 | 40 | from bzrlib import ( | ||
417 | 41 | revision, | ||
418 | 42 | trace, | ||
419 | 43 | ) | ||
420 | 44 | |||
421 | 45 | |||
422 | 46 | class LatestPublication(object): | ||
423 | 47 | """Encapsulate how to find the latest publication for a given project.""" | ||
424 | 48 | |||
425 | 49 | LP_API_ROOT = 'https://api.launchpad.net/1.0' | ||
426 | 50 | |||
427 | 51 | def __init__(self, archive, series, project): | ||
428 | 52 | self._archive = archive | ||
429 | 53 | self._project = project | ||
430 | 54 | self._setup_series_and_pocket(series) | ||
431 | 55 | |||
432 | 56 | def _setup_series_and_pocket(self, series): | ||
433 | 57 | """Parse the 'series' info into a series and a pocket. | ||
434 | 58 | |||
435 | 59 | eg:: | ||
436 | 60 | _setup_series_and_pocket('natty-proposed') | ||
437 | 61 | => _series == 'natty' | ||
438 | 62 | _pocket == 'Proposed' | ||
439 | 63 | """ | ||
440 | 64 | self._series = series | ||
441 | 65 | self._pocket = None | ||
442 | 66 | if self._series is not None and '-' in self._series: | ||
443 | 67 | self._series, self._pocket = self._series.split('-', 1) | ||
444 | 68 | self._pocket = self._pocket.title() | ||
445 | 69 | else: | ||
446 | 70 | self._pocket = 'Release' | ||
447 | 71 | |||
448 | 72 | def _archive_URL(self): | ||
449 | 73 | """Return the Launchpad 'Archive' URL that we will query. | ||
450 | 74 | This is everything in the URL except the query parameters. | ||
451 | 75 | """ | ||
452 | 76 | return '%s/%s/+archive/primary' % (self.LP_API_ROOT, self._archive) | ||
453 | 77 | |||
454 | 78 | def _publication_status(self): | ||
455 | 79 | """Handle the 'status' field. | ||
456 | 80 | It seems that Launchpad tracks all 'debian' packages as 'Pending', while | ||
457 | 81 | for 'ubuntu' we care about the 'Published' packages. | ||
458 | 82 | """ | ||
459 | 83 | if self._archive == 'debian': | ||
460 | 84 | # Launchpad only tracks debian packages as "Pending", it doesn't mark | ||
461 | 85 | # them Published | ||
462 | 86 | return 'Pending' | ||
463 | 87 | return 'Published' | ||
464 | 88 | |||
465 | 89 | def _query_params(self): | ||
466 | 90 | """Get the parameters defining our query. | ||
467 | 91 | This defines the actions we are making against the archive. | ||
468 | 92 | :return: A dict of query parameters. | ||
469 | 93 | """ | ||
470 | 94 | params = {'ws.op': 'getPublishedSources', | ||
471 | 95 | 'exact_match': 'true', | ||
472 | 96 | # If we need to use "" shouldn't we quote the project somehow? | ||
473 | 97 | 'source_name': '"%s"' % (self._project,), | ||
474 | 98 | 'status': self._publication_status(), | ||
475 | 99 | # We only need the latest one, the results seem to be properly | ||
476 | 100 | # most-recent-debian-version sorted | ||
477 | 101 | 'ws.size': '1', | ||
478 | 102 | } | ||
479 | 103 | if self._series is not None: | ||
480 | 104 | params['distro_series'] = '/%s/%s' % (self._archive, self._series) | ||
481 | 105 | if self._pocket is not None: | ||
482 | 106 | params['pocket'] = self._pocket | ||
483 | 107 | return params | ||
484 | 108 | |||
485 | 109 | def _query_URL(self): | ||
486 | 110 | """Create the full URL that we need to query, including parameters.""" | ||
487 | 111 | params = self._query_params() | ||
488 | 112 | # We sort to give deterministic results for testing | ||
489 | 113 | encoded = urllib.urlencode(sorted(params.items())) | ||
490 | 114 | return '%s?%s' % (self._archive_URL(), encoded) | ||
491 | 115 | |||
492 | 116 | def _get_lp_info(self): | ||
493 | 117 | """Place an actual HTTP query against the Launchpad service.""" | ||
494 | 118 | if json is None: | ||
495 | 119 | return None | ||
496 | 120 | query_URL = self._query_URL() | ||
497 | 121 | try: | ||
498 | 122 | req = urllib2.Request(query_URL) | ||
499 | 123 | response = urllib2.urlopen(req) | ||
500 | 124 | json_info = response.read() | ||
501 | 125 | # TODO: We haven't tested the HTTPError | ||
502 | 126 | except (urllib2.URLError, urllib2.HTTPError), e: | ||
503 | 127 | trace.mutter('failed to place query to %r' % (query_URL,)) | ||
504 | 128 | trace.log_exception_quietly() | ||
505 | 129 | return None | ||
506 | 130 | return json_info | ||
507 | 131 | |||
508 | 132 | def _parse_json_info(self, json_info): | ||
509 | 133 | """Parse the json response from Launchpad into objects.""" | ||
510 | 134 | if json is None: | ||
511 | 135 | return None | ||
512 | 136 | try: | ||
513 | 137 | return json.loads(json_info) | ||
514 | 138 | except Exception: | ||
515 | 139 | trace.mutter('Failed to parse json info: %r' % (json_info,)) | ||
516 | 140 | trace.log_exception_quietly() | ||
517 | 141 | return None | ||
518 | 142 | |||
519 | 143 | def get_latest_version(self): | ||
520 | 144 | """Get the latest published version for the given package.""" | ||
521 | 145 | json_info = self._get_lp_info() | ||
522 | 146 | if json_info is None: | ||
523 | 147 | return None | ||
524 | 148 | info = self._parse_json_info(json_info) | ||
525 | 149 | if info is None: | ||
526 | 150 | return None | ||
527 | 151 | try: | ||
528 | 152 | entries = info['entries'] | ||
529 | 153 | if len(entries) == 0: | ||
530 | 154 | return None | ||
531 | 155 | return entries[0]['source_package_version'] | ||
532 | 156 | except KeyError: | ||
533 | 157 | trace.log_exception_quietly() | ||
534 | 158 | return None | ||
535 | 159 | |||
536 | 160 | def place(self): | ||
537 | 161 | """Text-form for what location this represents. | ||
538 | 162 | |||
539 | 163 | Example:: | ||
540 | 164 | ubuntu, natty => Ubuntu Natty | ||
541 | 165 | ubuntu, natty-proposed => Ubuntu Natty Proposed | ||
542 | 166 | :return: A string representing the location we are checking. | ||
543 | 167 | """ | ||
544 | 168 | place = self._archive | ||
545 | 169 | if self._series is not None: | ||
546 | 170 | place = '%s %s' % (place, self._series) | ||
547 | 171 | if self._pocket is not None and self._pocket != 'Release': | ||
548 | 172 | place = '%s %s' % (place, self._pocket) | ||
549 | 173 | return place.title() | ||
550 | 174 | |||
551 | 175 | |||
552 | 176 | def get_latest_publication(archive, series, project): | ||
553 | 177 | """Get the most recent publication for a given project. | ||
554 | 178 | |||
555 | 179 | :param archive: Either 'ubuntu' or 'debian' | ||
556 | 180 | :param series: Something like 'natty', 'sid', etc. Can be set as None. Can | ||
557 | 181 | also include a pocket such as 'natty-proposed'. | ||
558 | 182 | :param project: Something like 'bzr' | ||
559 | 183 | :return: A version string indicating the most-recent version published in | ||
560 | 184 | Launchpad. Might return None if there is an error. | ||
561 | 185 | """ | ||
562 | 186 | lp = LatestPublication(archive, series, project) | ||
563 | 187 | return lp.get_latest_version() | ||
564 | 188 | |||
565 | 189 | |||
566 | 190 | def get_most_recent_tag(tag_dict, the_branch): | ||
567 | 191 | """Get the most recent revision that has been tagged.""" | ||
568 | 192 | # Note: this assumes that a given rev won't get tagged multiple times. But | ||
569 | 193 | # it should be valid for the package importer branches that we care | ||
570 | 194 | # about | ||
571 | 195 | reverse_dict = dict((rev, tag) for tag, rev in tag_dict.iteritems()) | ||
572 | 196 | the_branch.lock_read() | ||
573 | 197 | try: | ||
574 | 198 | history = the_branch.repository.iter_reverse_revision_history( | ||
575 | 199 | the_branch.last_revision()) | ||
576 | 200 | for rev_id in history: | ||
577 | 201 | if rev_id in reverse_dict: | ||
578 | 202 | return reverse_dict[rev_id] | ||
579 | 203 | finally: | ||
580 | 204 | the_branch.unlock() | ||
581 | 205 | |||
582 | 206 | |||
583 | 207 | def _get_newest_versions(the_branch, latest_pub): | ||
584 | 208 | """Get information about how 'fresh' this packaging branch is. | ||
585 | 209 | |||
586 | 210 | :param the_branch: The Branch to check | ||
587 | 211 | :param latest_pub: The LatestPublication used to check most recent | ||
588 | 212 | published version. | ||
589 | 213 | :return: (latest_ver, branch_latest_ver) | ||
590 | 214 | """ | ||
591 | 215 | t = time.time() | ||
592 | 216 | latest_ver = latest_pub.get_latest_version() | ||
593 | 217 | t_latest_ver = time.time() - t | ||
594 | 218 | trace.mutter('LatestPublication.get_latest_version took: %.3fs' | ||
595 | 219 | % (t_latest_ver,)) | ||
596 | 220 | if latest_ver is None: | ||
597 | 221 | return None, None | ||
598 | 222 | t = time.time() | ||
599 | 223 | tags = the_branch.tags.get_tag_dict() | ||
600 | 224 | t_tag_dict = time.time() - t | ||
601 | 225 | trace.mutter('LatestPublication.get_tag_dict took: %.3fs' % (t_tag_dict,)) | ||
602 | 226 | if latest_ver in tags: | ||
603 | 227 | # branch might have a newer tag, but we don't really care | ||
604 | 228 | return latest_ver, latest_ver | ||
605 | 229 | else: | ||
606 | 230 | best_tag = get_most_recent_tag(tags, the_branch) | ||
607 | 231 | return latest_ver, best_tag | ||
608 | 232 | |||
609 | 233 | |||
610 | 234 | def _report_freshness(latest_ver, branch_latest_ver, place, verbosity, | ||
611 | 235 | report_func): | ||
612 | 236 | """Report if the branch is up-to-date.""" | ||
613 | 237 | if latest_ver is None: | ||
614 | 238 | if verbosity == 'all': | ||
615 | 239 | report_func('Most recent %s version: MISSING' % (place,)) | ||
616 | 240 | elif verbosity == 'short': | ||
617 | 241 | report_func('%s is MISSING a version' % (place,)) | ||
618 | 242 | return | ||
619 | 243 | elif latest_ver == branch_latest_ver: | ||
620 | 244 | if verbosity == 'minimal': | ||
621 | 245 | return | ||
622 | 246 | elif verbosity == 'short': | ||
623 | 247 | report_func('%s is CURRENT in %s' % (latest_ver, place)) | ||
624 | 248 | else: | ||
625 | 249 | report_func('Most recent %s version: %s\n' | ||
626 | 250 | 'Packaging branch status: CURRENT' | ||
627 | 251 | % (place, latest_ver)) | ||
628 | 252 | else: | ||
629 | 253 | if verbosity in ('minimal', 'short'): | ||
630 | 254 | if branch_latest_ver is None: | ||
631 | 255 | branch_latest_ver = 'Branch' | ||
632 | 256 | report_func('%s is OUT-OF-DATE, %s has %s' | ||
633 | 257 | % (branch_latest_ver, place, latest_ver)) | ||
634 | 258 | else: | ||
635 | 259 | report_func('Most recent %s version: %s\n' | ||
636 | 260 | 'Packaging branch version: %s\n' | ||
637 | 261 | 'Packaging branch status: OUT-OF-DATE' | ||
638 | 262 | % (place, latest_ver, branch_latest_ver)) | ||
639 | 263 | |||
640 | 264 | |||
641 | 265 | def report_freshness(the_branch, verbosity, latest_pub): | ||
642 | 266 | """Report to the user how up-to-date the packaging branch is. | ||
643 | 267 | |||
644 | 268 | :param the_branch: A Branch object | ||
645 | 269 | :param verbosity: Can be one of: | ||
646 | 270 | off: Do not print anything, and skip all checks. | ||
647 | 271 | all: Print all information that we have in a verbose manner, this | ||
648 | 272 | includes misses, etc. | ||
649 | 273 | short: Print information, but only one-line summaries | ||
650 | 274 | minimal: Only print a one-line summary when the package branch is | ||
651 | 275 | out-of-date | ||
652 | 276 | :param latest_pub: A LatestPublication instance | ||
653 | 277 | """ | ||
654 | 278 | if verbosity == 'off': | ||
655 | 279 | return | ||
656 | 280 | if verbosity is None: | ||
657 | 281 | verbosity = 'all' | ||
658 | 282 | latest_ver, branch_ver = _get_newest_versions(the_branch, latest_pub) | ||
659 | 283 | place = latest_pub.place() | ||
660 | 284 | _report_freshness(latest_ver, branch_ver, place, verbosity, | ||
661 | 285 | trace.note) | ||
662 | 0 | 286 | ||
663 | === added file 'bzrlib/plugins/launchpad/test_lp_api_lite.py' | |||
664 | --- bzrlib/plugins/launchpad/test_lp_api_lite.py 1970-01-01 00:00:00 +0000 | |||
665 | +++ bzrlib/plugins/launchpad/test_lp_api_lite.py 2011-09-27 11:59:22 +0000 | |||
666 | @@ -0,0 +1,557 @@ | |||
667 | 1 | # Copyright (C) 2011 Canonical Ltd | ||
668 | 2 | # | ||
669 | 3 | # This program is free software; you can redistribute it and/or modify | ||
670 | 4 | # it under the terms of the GNU General Public License as published by | ||
671 | 5 | # the Free Software Foundation; either version 2 of the License, or | ||
672 | 6 | # (at your option) any later version. | ||
673 | 7 | # | ||
674 | 8 | # This program is distributed in the hope that it will be useful, | ||
675 | 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
676 | 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
677 | 11 | # GNU General Public License for more details. | ||
678 | 12 | # | ||
679 | 13 | # You should have received a copy of the GNU General Public License | ||
680 | 14 | # along with this program; if not, write to the Free Software | ||
681 | 15 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
682 | 16 | |||
683 | 17 | """Tools for dealing with the Launchpad API without using launchpadlib. | ||
684 | 18 | """ | ||
685 | 19 | |||
686 | 20 | import doctest | ||
687 | 21 | import socket | ||
688 | 22 | |||
689 | 23 | from bzrlib import tests | ||
690 | 24 | from bzrlib.plugins import launchpad | ||
691 | 25 | from bzrlib.plugins.launchpad import lp_api_lite | ||
692 | 26 | |||
693 | 27 | from testtools.matchers import DocTestMatches | ||
694 | 28 | |||
695 | 29 | |||
696 | 30 | class _JSONParserFeature(tests.Feature): | ||
697 | 31 | |||
698 | 32 | def _probe(self): | ||
699 | 33 | return lp_api_lite.json is not None | ||
700 | 34 | |||
701 | 35 | def feature_name(self): | ||
702 | 36 | return 'simplejson or json' | ||
703 | 37 | |||
704 | 38 | JSONParserFeature = _JSONParserFeature() | ||
705 | 39 | |||
706 | 40 | _example_response = r""" | ||
707 | 41 | { | ||
708 | 42 | "total_size": 2, | ||
709 | 43 | "start": 0, | ||
710 | 44 | "next_collection_link": "https://api.launchpad.net/1.0/ubuntu/+archive/primary?distro_series=%2Fubuntu%2Flucid&exact_match=true&source_name=%22bzr%22&status=Published&ws.op=getPublishedSources&ws.start=1&ws.size=1", | ||
711 | 45 | "entries": [ | ||
712 | 46 | { | ||
713 | 47 | "package_creator_link": "https://api.launchpad.net/1.0/~maxb", | ||
714 | 48 | "package_signer_link": "https://api.launchpad.net/1.0/~jelmer", | ||
715 | 49 | "source_package_name": "bzr", | ||
716 | 50 | "removal_comment": null, | ||
717 | 51 | "display_name": "bzr 2.1.4-0ubuntu1 in lucid", | ||
718 | 52 | "date_made_pending": null, | ||
719 | 53 | "source_package_version": "2.1.4-0ubuntu1", | ||
720 | 54 | "date_superseded": null, | ||
721 | 55 | "http_etag": "\"9ba966152dec474dc0fe1629d0bbce2452efaf3b-5f4c3fbb3eaf26d502db4089777a9b6a0537ffab\"", | ||
722 | 56 | "self_link": "https://api.launchpad.net/1.0/ubuntu/+archive/primary/+sourcepub/1750327", | ||
723 | 57 | "distro_series_link": "https://api.launchpad.net/1.0/ubuntu/lucid", | ||
724 | 58 | "component_name": "main", | ||
725 | 59 | "status": "Published", | ||
726 | 60 | "date_removed": null, | ||
727 | 61 | "pocket": "Updates", | ||
728 | 62 | "date_published": "2011-05-30T06:09:58.653984+00:00", | ||
729 | 63 | "removed_by_link": null, | ||
730 | 64 | "section_name": "devel", | ||
731 | 65 | "resource_type_link": "https://api.launchpad.net/1.0/#source_package_publishing_history", | ||
732 | 66 | "archive_link": "https://api.launchpad.net/1.0/ubuntu/+archive/primary", | ||
733 | 67 | "package_maintainer_link": "https://api.launchpad.net/1.0/~ubuntu-devel-discuss-lists", | ||
734 | 68 | "date_created": "2011-05-30T05:19:12.233621+00:00", | ||
735 | 69 | "scheduled_deletion_date": null | ||
736 | 70 | } | ||
737 | 71 | ] | ||
738 | 72 | }""" | ||
739 | 73 | |||
740 | 74 | _no_versions_response = '{"total_size": 0, "start": 0, "entries": []}' | ||
741 | 75 | |||
742 | 76 | |||
743 | 77 | class TestLatestPublication(tests.TestCase): | ||
744 | 78 | |||
745 | 79 | def make_latest_publication(self, archive='ubuntu', series='natty', | ||
746 | 80 | project='bzr'): | ||
747 | 81 | return lp_api_lite.LatestPublication(archive, series, project) | ||
748 | 82 | |||
749 | 83 | def assertPlace(self, place, archive, series, project): | ||
750 | 84 | lp = lp_api_lite.LatestPublication(archive, series, project) | ||
751 | 85 | self.assertEqual(place, lp.place()) | ||
752 | 86 | |||
753 | 87 | def test_init(self): | ||
754 | 88 | latest_pub = self.make_latest_publication() | ||
755 | 89 | self.assertEqual('ubuntu', latest_pub._archive) | ||
756 | 90 | self.assertEqual('natty', latest_pub._series) | ||
757 | 91 | self.assertEqual('bzr', latest_pub._project) | ||
758 | 92 | self.assertEqual('Release', latest_pub._pocket) | ||
759 | 93 | |||
760 | 94 | def test__archive_URL(self): | ||
761 | 95 | latest_pub = self.make_latest_publication() | ||
762 | 96 | self.assertEqual( | ||
763 | 97 | 'https://api.launchpad.net/1.0/ubuntu/+archive/primary', | ||
764 | 98 | latest_pub._archive_URL()) | ||
765 | 99 | |||
766 | 100 | def test__publication_status_for_ubuntu(self): | ||
767 | 101 | latest_pub = self.make_latest_publication() | ||
768 | 102 | self.assertEqual('Published', latest_pub._publication_status()) | ||
769 | 103 | |||
770 | 104 | def test__publication_status_for_debian(self): | ||
771 | 105 | latest_pub = self.make_latest_publication(archive='debian') | ||
772 | 106 | self.assertEqual('Pending', latest_pub._publication_status()) | ||
773 | 107 | |||
774 | 108 | def test_pocket(self): | ||
775 | 109 | latest_pub = self.make_latest_publication(series='natty-proposed') | ||
776 | 110 | self.assertEqual('natty', latest_pub._series) | ||
777 | 111 | self.assertEqual('Proposed', latest_pub._pocket) | ||
778 | 112 | |||
779 | 113 | def test_series_None(self): | ||
780 | 114 | latest_pub = self.make_latest_publication(series=None) | ||
781 | 115 | self.assertEqual('ubuntu', latest_pub._archive) | ||
782 | 116 | self.assertEqual(None, latest_pub._series) | ||
783 | 117 | self.assertEqual('bzr', latest_pub._project) | ||
784 | 118 | self.assertEqual('Release', latest_pub._pocket) | ||
785 | 119 | |||
786 | 120 | def test__query_params(self): | ||
787 | 121 | latest_pub = self.make_latest_publication() | ||
788 | 122 | self.assertEqual({'ws.op': 'getPublishedSources', | ||
789 | 123 | 'exact_match': 'true', | ||
790 | 124 | 'source_name': '"bzr"', | ||
791 | 125 | 'status': 'Published', | ||
792 | 126 | 'ws.size': '1', | ||
793 | 127 | 'distro_series': '/ubuntu/natty', | ||
794 | 128 | 'pocket': 'Release', | ||
795 | 129 | }, latest_pub._query_params()) | ||
796 | 130 | |||
797 | 131 | def test__query_params_no_series(self): | ||
798 | 132 | latest_pub = self.make_latest_publication(series=None) | ||
799 | 133 | self.assertEqual({'ws.op': 'getPublishedSources', | ||
800 | 134 | 'exact_match': 'true', | ||
801 | 135 | 'source_name': '"bzr"', | ||
802 | 136 | 'status': 'Published', | ||
803 | 137 | 'ws.size': '1', | ||
804 | 138 | 'pocket': 'Release', | ||
805 | 139 | }, latest_pub._query_params()) | ||
806 | 140 | |||
807 | 141 | def test__query_params_pocket(self): | ||
808 | 142 | latest_pub = self.make_latest_publication(series='natty-proposed') | ||
809 | 143 | self.assertEqual({'ws.op': 'getPublishedSources', | ||
810 | 144 | 'exact_match': 'true', | ||
811 | 145 | 'source_name': '"bzr"', | ||
812 | 146 | 'status': 'Published', | ||
813 | 147 | 'ws.size': '1', | ||
814 | 148 | 'distro_series': '/ubuntu/natty', | ||
815 | 149 | 'pocket': 'Proposed', | ||
816 | 150 | }, latest_pub._query_params()) | ||
817 | 151 | |||
818 | 152 | def test__query_URL(self): | ||
819 | 153 | latest_pub = self.make_latest_publication() | ||
820 | 154 | # we explicitly sort params, so we can be sure this URL matches exactly | ||
821 | 155 | self.assertEqual( | ||
822 | 156 | 'https://api.launchpad.net/1.0/ubuntu/+archive/primary' | ||
823 | 157 | '?distro_series=%2Fubuntu%2Fnatty&exact_match=true' | ||
824 | 158 | '&pocket=Release&source_name=%22bzr%22&status=Published' | ||
825 | 159 | '&ws.op=getPublishedSources&ws.size=1', | ||
826 | 160 | latest_pub._query_URL()) | ||
827 | 161 | |||
828 | 162 | def DONT_test__gracefully_handle_failed_rpc_connection(self): | ||
829 | 163 | # TODO: This test kind of sucks. We intentionally create an arbitrary | ||
830 | 164 | # port and don't listen to it, because we want the request to fail. | ||
831 | 165 | # However, it seems to take 1s for it to timeout. Is there a way | ||
832 | 166 | # to make it fail faster? | ||
833 | 167 | latest_pub = self.make_latest_publication() | ||
834 | 168 | s = socket.socket() | ||
835 | 169 | s.bind(('127.0.0.1', 0)) | ||
836 | 170 | addr, port = s.getsockname() | ||
837 | 171 | latest_pub.LP_API_ROOT = 'http://%s:%s/' % (addr, port) | ||
838 | 172 | s.close() | ||
839 | 173 | self.assertIs(None, latest_pub._get_lp_info()) | ||
840 | 174 | |||
841 | 175 | def DONT_test__query_launchpad(self): | ||
842 | 176 | # TODO: This is a test that we are making a valid request against | ||
843 | 177 | # launchpad. This seems important, but it is slow, requires net | ||
844 | 178 | # access, and requires launchpad to be up and running. So for | ||
845 | 179 | # now, it is commented out for production tests. | ||
846 | 180 | latest_pub = self.make_latest_publication() | ||
847 | 181 | json_txt = latest_pub._get_lp_info() | ||
848 | 182 | self.assertIsNot(None, json_txt) | ||
849 | 183 | if lp_api_lite.json is None: | ||
850 | 184 | # We don't have a way to parse the text | ||
851 | 185 | return | ||
852 | 186 | # The content should be a valid json result | ||
853 | 187 | content = lp_api_lite.json.loads(json_txt) | ||
854 | 188 | entries = content['entries'] # It should have an 'entries' field. | ||
855 | 189 | # ws.size should mean we get 0 or 1, and there should be something | ||
856 | 190 | self.assertEqual(1, len(entries)) | ||
857 | 191 | entry = entries[0] | ||
858 | 192 | self.assertEqual('bzr', entry['source_package_name']) | ||
859 | 193 | version = entry['source_package_version'] | ||
860 | 194 | self.assertIsNot(None, version) | ||
861 | 195 | |||
862 | 196 | def disableJSON(self): | ||
863 | 197 | orig = lp_api_lite.json | ||
864 | 198 | def cleanup(): | ||
865 | 199 | lp_api_lite.json = orig | ||
866 | 200 | self.addCleanup(cleanup) | ||
867 | 201 | lp_api_lite.json = None | ||
868 | 202 | |||
869 | 203 | def test__get_lp_info_no_json(self): | ||
870 | 204 | # If we can't parse the json, we don't make the query. | ||
871 | 205 | self.disableJSON() | ||
872 | 206 | latest_pub = self.make_latest_publication() | ||
873 | 207 | self.assertIs(None, latest_pub._get_lp_info()) | ||
874 | 208 | |||
875 | 209 | def test__parse_json_info_no_module(self): | ||
876 | 210 | # If a json parsing module isn't available, we just return None here. | ||
877 | 211 | self.disableJSON() | ||
878 | 212 | latest_pub = self.make_latest_publication() | ||
879 | 213 | self.assertIs(None, latest_pub._parse_json_info(_example_response)) | ||
880 | 214 | |||
881 | 215 | def test__parse_json_example_response(self): | ||
882 | 216 | self.requireFeature(JSONParserFeature) | ||
883 | 217 | latest_pub = self.make_latest_publication() | ||
884 | 218 | content = latest_pub._parse_json_info(_example_response) | ||
885 | 219 | self.assertIsNot(None, content) | ||
886 | 220 | self.assertEqual(2, content['total_size']) | ||
887 | 221 | entries = content['entries'] | ||
888 | 222 | self.assertEqual(1, len(entries)) | ||
889 | 223 | entry = entries[0] | ||
890 | 224 | self.assertEqual('bzr', entry['source_package_name']) | ||
891 | 225 | self.assertEqual("2.1.4-0ubuntu1", entry["source_package_version"]) | ||
892 | 226 | |||
893 | 227 | def test__parse_json_not_json(self): | ||
894 | 228 | self.requireFeature(JSONParserFeature) | ||
895 | 229 | latest_pub = self.make_latest_publication() | ||
896 | 230 | self.assertIs(None, latest_pub._parse_json_info('Not_valid_json')) | ||
897 | 231 | |||
898 | 232 | def test_get_latest_version_no_response(self): | ||
899 | 233 | latest_pub = self.make_latest_publication() | ||
900 | 234 | latest_pub._get_lp_info = lambda: None | ||
901 | 235 | self.assertEqual(None, latest_pub.get_latest_version()) | ||
902 | 236 | |||
903 | 237 | def test_get_latest_version_no_json(self): | ||
904 | 238 | self.disableJSON() | ||
905 | 239 | latest_pub = self.make_latest_publication() | ||
906 | 240 | self.assertEqual(None, latest_pub.get_latest_version()) | ||
907 | 241 | |||
908 | 242 | def test_get_latest_version_invalid_json(self): | ||
909 | 243 | self.requireFeature(JSONParserFeature) | ||
910 | 244 | latest_pub = self.make_latest_publication() | ||
911 | 245 | latest_pub._get_lp_info = lambda: "not json" | ||
912 | 246 | self.assertEqual(None, latest_pub.get_latest_version()) | ||
913 | 247 | |||
914 | 248 | def test_get_latest_version_no_versions(self): | ||
915 | 249 | self.requireFeature(JSONParserFeature) | ||
916 | 250 | latest_pub = self.make_latest_publication() | ||
917 | 251 | latest_pub._get_lp_info = lambda: _no_versions_response | ||
918 | 252 | self.assertEqual(None, latest_pub.get_latest_version()) | ||
919 | 253 | |||
920 | 254 | def test_get_latest_version_missing_entries(self): | ||
921 | 255 | # Launchpad's no-entries response does have an empty entries value. | ||
922 | 256 | # However, lets test that we handle other failures without tracebacks | ||
923 | 257 | self.requireFeature(JSONParserFeature) | ||
924 | 258 | latest_pub = self.make_latest_publication() | ||
925 | 259 | latest_pub._get_lp_info = lambda: '{}' | ||
926 | 260 | self.assertEqual(None, latest_pub.get_latest_version()) | ||
927 | 261 | |||
928 | 262 | def test_get_latest_version_invalid_entries(self): | ||
929 | 263 | # Make sure we sanely handle a json response we don't understand | ||
930 | 264 | self.requireFeature(JSONParserFeature) | ||
931 | 265 | latest_pub = self.make_latest_publication() | ||
932 | 266 | latest_pub._get_lp_info = lambda: '{"entries": {"a": 1}}' | ||
933 | 267 | self.assertEqual(None, latest_pub.get_latest_version()) | ||
934 | 268 | |||
935 | 269 | def test_get_latest_version_example(self): | ||
936 | 270 | self.requireFeature(JSONParserFeature) | ||
937 | 271 | latest_pub = self.make_latest_publication() | ||
938 | 272 | latest_pub._get_lp_info = lambda: _example_response | ||
939 | 273 | self.assertEqual("2.1.4-0ubuntu1", latest_pub.get_latest_version()) | ||
940 | 274 | |||
941 | 275 | def DONT_test_get_latest_version_from_launchpad(self): | ||
942 | 276 | self.requireFeature(JSONParserFeature) | ||
943 | 277 | latest_pub = self.make_latest_publication() | ||
944 | 278 | self.assertIsNot(None, latest_pub.get_latest_version()) | ||
945 | 279 | |||
946 | 280 | def test_place(self): | ||
947 | 281 | self.assertPlace('Ubuntu', 'ubuntu', None, 'bzr') | ||
948 | 282 | self.assertPlace('Ubuntu Natty', 'ubuntu', 'natty', 'bzr') | ||
949 | 283 | self.assertPlace('Ubuntu Natty Proposed', 'ubuntu', 'natty-proposed', | ||
950 | 284 | 'bzr') | ||
951 | 285 | self.assertPlace('Debian', 'debian', None, 'bzr') | ||
952 | 286 | self.assertPlace('Debian Sid', 'debian', 'sid', 'bzr') | ||
953 | 287 | |||
954 | 288 | |||
955 | 289 | class TestIsUpToDate(tests.TestCase): | ||
956 | 290 | |||
957 | 291 | def assertPackageBranchRe(self, url, user, archive, series, project): | ||
958 | 292 | m = launchpad._package_branch.search(url) | ||
959 | 293 | if m is None: | ||
960 | 294 | self.fail('package_branch regex did not match url: %s' % (url,)) | ||
961 | 295 | self.assertEqual( | ||
962 | 296 | (user, archive, series, project), | ||
963 | 297 | m.group('user', 'archive', 'series', 'project')) | ||
964 | 298 | |||
965 | 299 | def assertNotPackageBranch(self, url): | ||
966 | 300 | self.assertIs(None, launchpad._get_package_branch_info(url)) | ||
967 | 301 | |||
968 | 302 | def assertBranchInfo(self, url, archive, series, project): | ||
969 | 303 | self.assertEqual((archive, series, project), | ||
970 | 304 | launchpad._get_package_branch_info(url)) | ||
971 | 305 | |||
972 | 306 | def test_package_branch_regex(self): | ||
973 | 307 | self.assertPackageBranchRe( | ||
974 | 308 | 'http://bazaar.launchpad.net/+branch/ubuntu/foo', | ||
975 | 309 | None, 'ubuntu', None, 'foo') | ||
976 | 310 | self.assertPackageBranchRe( | ||
977 | 311 | 'bzr+ssh://bazaar.launchpad.net/+branch/ubuntu/natty/foo', | ||
978 | 312 | None, 'ubuntu', 'natty/', 'foo') | ||
979 | 313 | self.assertPackageBranchRe( | ||
980 | 314 | 'sftp://bazaar.launchpad.net/+branch/debian/foo', | ||
981 | 315 | None, 'debian', None, 'foo') | ||
982 | 316 | self.assertPackageBranchRe( | ||
983 | 317 | 'http://bazaar.launchpad.net/+branch/debian/sid/foo', | ||
984 | 318 | None, 'debian', 'sid/', 'foo') | ||
985 | 319 | self.assertPackageBranchRe( | ||
986 | 320 | 'http://bazaar.launchpad.net/+branch' | ||
987 | 321 | '/~ubuntu-branches/ubuntu/natty/foo/natty', | ||
988 | 322 | '~ubuntu-branches/', 'ubuntu', 'natty/', 'foo') | ||
989 | 323 | self.assertPackageBranchRe( | ||
990 | 324 | 'http://bazaar.launchpad.net/+branch' | ||
991 | 325 | '/~user/ubuntu/natty/foo/test', | ||
992 | 326 | '~user/', 'ubuntu', 'natty/', 'foo') | ||
993 | 327 | |||
994 | 328 | def test_package_branch_doesnt_match(self): | ||
995 | 329 | self.assertNotPackageBranch('http://example.com/ubuntu/foo') | ||
996 | 330 | self.assertNotPackageBranch( | ||
997 | 331 | 'http://bazaar.launchpad.net/+branch/bzr') | ||
998 | 332 | self.assertNotPackageBranch( | ||
999 | 333 | 'http://bazaar.launchpad.net/+branch/~bzr-pqm/bzr/bzr.dev') | ||
1000 | 334 | # Not a packaging branch because ~user isn't ~ubuntu-branches | ||
1001 | 335 | self.assertNotPackageBranch( | ||
1002 | 336 | 'http://bazaar.launchpad.net/+branch' | ||
1003 | 337 | '/~user/ubuntu/natty/foo/natty') | ||
1004 | 338 | # Older versions of bzr-svn/hg/git did not set Branch.base until after | ||
1005 | 339 | # they called Branch.__init__(). | ||
1006 | 340 | self.assertNotPackageBranch(None) | ||
1007 | 341 | |||
1008 | 342 | def test__get_package_branch_info(self): | ||
1009 | 343 | self.assertBranchInfo( | ||
1010 | 344 | 'bzr+ssh://bazaar.launchpad.net/+branch/ubuntu/natty/foo', | ||
1011 | 345 | 'ubuntu', 'natty', 'foo') | ||
1012 | 346 | self.assertBranchInfo( | ||
1013 | 347 | 'bzr+ssh://bazaar.launchpad.net/+branch' | ||
1014 | 348 | '/~ubuntu-branches/ubuntu/natty/foo/natty', | ||
1015 | 349 | 'ubuntu', 'natty', 'foo') | ||
1016 | 350 | self.assertBranchInfo( | ||
1017 | 351 | 'http://bazaar.launchpad.net/+branch' | ||
1018 | 352 | '/~ubuntu-branches/debian/sid/foo/sid', | ||
1019 | 353 | 'debian', 'sid', 'foo') | ||
1020 | 354 | |||
1021 | 355 | |||
1022 | 356 | class TestGetMostRecentTag(tests.TestCaseWithMemoryTransport): | ||
1023 | 357 | |||
1024 | 358 | def make_simple_builder(self): | ||
1025 | 359 | builder = self.make_branch_builder('tip') | ||
1026 | 360 | builder.build_snapshot('A', None, [ | ||
1027 | 361 | ('add', ('', 'root-id', 'directory', None))]) | ||
1028 | 362 | b = builder.get_branch() | ||
1029 | 363 | b.tags.set_tag('tip-1.0', 'A') | ||
1030 | 364 | return builder, b, b.tags.get_tag_dict() | ||
1031 | 365 | |||
1032 | 366 | def test_get_most_recent_tag_tip(self): | ||
1033 | 367 | builder, b, tag_dict = self.make_simple_builder() | ||
1034 | 368 | self.assertEqual('tip-1.0', | ||
1035 | 369 | lp_api_lite.get_most_recent_tag(tag_dict, b)) | ||
1036 | 370 | |||
1037 | 371 | def test_get_most_recent_tag_older(self): | ||
1038 | 372 | builder, b, tag_dict = self.make_simple_builder() | ||
1039 | 373 | builder.build_snapshot('B', ['A'], []) | ||
1040 | 374 | self.assertEqual('B', b.last_revision()) | ||
1041 | 375 | self.assertEqual('tip-1.0', | ||
1042 | 376 | lp_api_lite.get_most_recent_tag(tag_dict, b)) | ||
1043 | 377 | |||
1044 | 378 | |||
1045 | 379 | class StubLatestPublication(object): | ||
1046 | 380 | |||
1047 | 381 | def __init__(self, latest): | ||
1048 | 382 | self.called = False | ||
1049 | 383 | self.latest = latest | ||
1050 | 384 | |||
1051 | 385 | def get_latest_version(self): | ||
1052 | 386 | self.called = True | ||
1053 | 387 | return self.latest | ||
1054 | 388 | |||
1055 | 389 | def place(self): | ||
1056 | 390 | return 'Ubuntu Natty' | ||
1057 | 391 | |||
1058 | 392 | |||
1059 | 393 | class TestReportFreshness(tests.TestCaseWithMemoryTransport): | ||
1060 | 394 | |||
1061 | 395 | def setUp(self): | ||
1062 | 396 | super(TestReportFreshness, self).setUp() | ||
1063 | 397 | builder = self.make_branch_builder('tip') | ||
1064 | 398 | builder.build_snapshot('A', None, [ | ||
1065 | 399 | ('add', ('', 'root-id', 'directory', None))]) | ||
1066 | 400 | self.branch = builder.get_branch() | ||
1067 | 401 | |||
1068 | 402 | def assertFreshnessReports(self, verbosity, latest_version, content): | ||
1069 | 403 | """Assert that lp_api_lite.report_freshness reports the given content. | ||
1070 | 404 | |||
1071 | 405 | :param verbosity: The reporting level | ||
1072 | 406 | :param latest_version: The version reported by StubLatestPublication | ||
1073 | 407 | :param content: The expected content. This should be in DocTest form. | ||
1074 | 408 | """ | ||
1075 | 409 | orig_log_len = len(self.get_log()) | ||
1076 | 410 | lp_api_lite.report_freshness(self.branch, verbosity, | ||
1077 | 411 | StubLatestPublication(latest_version)) | ||
1078 | 412 | new_content = self.get_log()[orig_log_len:] | ||
1079 | 413 | # Strip out lines that have LatestPublication.get_* because those are | ||
1080 | 414 | # timing related lines. While interesting to log for now, they aren't | ||
1081 | 415 | # something we want to be testing | ||
1082 | 416 | new_content = new_content.split('\n') | ||
1083 | 417 | for i in range(2): | ||
1084 | 418 | if (len(new_content) > 0 | ||
1085 | 419 | and 'LatestPublication.get_' in new_content[0]): | ||
1086 | 420 | new_content = new_content[1:] | ||
1087 | 421 | new_content = '\n'.join(new_content) | ||
1088 | 422 | self.assertThat(new_content, | ||
1089 | 423 | DocTestMatches(content, | ||
1090 | 424 | doctest.ELLIPSIS | doctest.REPORT_UDIFF)) | ||
1091 | 425 | |||
1092 | 426 | def test_verbosity_off_skips_check(self): | ||
1093 | 427 | # We force _get_package_branch_info so that we know it would otherwise | ||
1094 | 428 | # try to connect to launcphad | ||
1095 | 429 | orig_gpbi = launchpad._get_package_branch_info | ||
1096 | 430 | orig_lp = lp_api_lite.LatestPublication | ||
1097 | 431 | def cleanup(): | ||
1098 | 432 | launchpad._get_package_branch_info = orig_gpbi | ||
1099 | 433 | lp_api_lite.LatestPublication = orig_lp | ||
1100 | 434 | self.addCleanup(cleanup) | ||
1101 | 435 | launchpad._get_package_branch_info = lambda x: ('ubuntu', 'natty', 'bzr') | ||
1102 | 436 | lp_api_lite.LatestPublication = lambda *args: self.fail('Tried to query launchpad') | ||
1103 | 437 | c = self.branch.get_config() | ||
1104 | 438 | c.set_user_option('launchpad.packaging_verbosity', 'off') | ||
1105 | 439 | orig_log_len = len(self.get_log()) | ||
1106 | 440 | launchpad._check_is_up_to_date(self.branch) | ||
1107 | 441 | new_content = self.get_log()[orig_log_len:] | ||
1108 | 442 | self.assertContainsRe(new_content, | ||
1109 | 443 | 'not checking memory.*/tip/ because verbosity is turned off') | ||
1110 | 444 | |||
1111 | 445 | def test_verbosity_off(self): | ||
1112 | 446 | latest_pub = StubLatestPublication('1.0-1ubuntu2') | ||
1113 | 447 | lp_api_lite.report_freshness(self.branch, 'off', latest_pub) | ||
1114 | 448 | self.assertFalse(latest_pub.called) | ||
1115 | 449 | |||
1116 | 450 | def test_verbosity_all_out_of_date_smoke(self): | ||
1117 | 451 | self.branch.tags.set_tag('1.0-1ubuntu1', 'A') | ||
1118 | 452 | self.assertFreshnessReports('all', '1.0-1ubuntu2', | ||
1119 | 453 | ' INFO Most recent Ubuntu Natty version: 1.0-1ubuntu2\n' | ||
1120 | 454 | 'Packaging branch version: 1.0-1ubuntu1\n' | ||
1121 | 455 | 'Packaging branch status: OUT-OF-DATE\n') | ||
1122 | 456 | |||
1123 | 457 | |||
1124 | 458 | class Test_GetNewestVersions(tests.TestCaseWithMemoryTransport): | ||
1125 | 459 | |||
1126 | 460 | def setUp(self): | ||
1127 | 461 | super(Test_GetNewestVersions, self).setUp() | ||
1128 | 462 | builder = self.make_branch_builder('tip') | ||
1129 | 463 | builder.build_snapshot('A', None, [ | ||
1130 | 464 | ('add', ('', 'root-id', 'directory', None))]) | ||
1131 | 465 | self.branch = builder.get_branch() | ||
1132 | 466 | |||
1133 | 467 | def assertLatestVersions(self, latest_branch_version, pub_version): | ||
1134 | 468 | if latest_branch_version is not None: | ||
1135 | 469 | self.branch.tags.set_tag(latest_branch_version, 'A') | ||
1136 | 470 | latest_pub = StubLatestPublication(pub_version) | ||
1137 | 471 | self.assertEqual((pub_version, latest_branch_version), | ||
1138 | 472 | lp_api_lite._get_newest_versions(self.branch, latest_pub)) | ||
1139 | 473 | |||
1140 | 474 | def test_no_tags(self): | ||
1141 | 475 | self.assertLatestVersions(None, '1.0-1ubuntu2') | ||
1142 | 476 | |||
1143 | 477 | def test_out_of_date(self): | ||
1144 | 478 | self.assertLatestVersions('1.0-1ubuntu1', '1.0-1ubuntu2') | ||
1145 | 479 | |||
1146 | 480 | def test_up_to_date(self): | ||
1147 | 481 | self.assertLatestVersions('1.0-1ubuntu2', '1.0-1ubuntu2') | ||
1148 | 482 | |||
1149 | 483 | def test_missing(self): | ||
1150 | 484 | self.assertLatestVersions(None, None) | ||
1151 | 485 | |||
1152 | 486 | |||
1153 | 487 | class Test_ReportFreshness(tests.TestCase): | ||
1154 | 488 | |||
1155 | 489 | def assertReportedFreshness(self, verbosity, latest_ver, branch_latest_ver, | ||
1156 | 490 | content, place='Ubuntu Natty'): | ||
1157 | 491 | """Assert that lp_api_lite.report_freshness reports the given content. | ||
1158 | 492 | """ | ||
1159 | 493 | reported = [] | ||
1160 | 494 | def report_func(value): | ||
1161 | 495 | reported.append(value) | ||
1162 | 496 | lp_api_lite._report_freshness(latest_ver, branch_latest_ver, place, | ||
1163 | 497 | verbosity, report_func) | ||
1164 | 498 | new_content = '\n'.join(reported) | ||
1165 | 499 | self.assertThat(new_content, | ||
1166 | 500 | DocTestMatches(content, | ||
1167 | 501 | doctest.ELLIPSIS | doctest.REPORT_UDIFF)) | ||
1168 | 502 | |||
1169 | 503 | def test_verbosity_minimal_no_tags(self): | ||
1170 | 504 | self.assertReportedFreshness('minimal', '1.0-1ubuntu2', None, | ||
1171 | 505 | 'Branch is OUT-OF-DATE, Ubuntu Natty has 1.0-1ubuntu2\n') | ||
1172 | 506 | |||
1173 | 507 | def test_verbosity_minimal_out_of_date(self): | ||
1174 | 508 | self.assertReportedFreshness('minimal', '1.0-1ubuntu2', '1.0-1ubuntu1', | ||
1175 | 509 | '1.0-1ubuntu1 is OUT-OF-DATE,' | ||
1176 | 510 | ' Ubuntu Natty has 1.0-1ubuntu2\n') | ||
1177 | 511 | |||
1178 | 512 | def test_verbosity_minimal_up_to_date(self): | ||
1179 | 513 | self.assertReportedFreshness('minimal', '1.0-1ubuntu2', '1.0-1ubuntu2', | ||
1180 | 514 | '') | ||
1181 | 515 | |||
1182 | 516 | def test_verbosity_minimal_missing(self): | ||
1183 | 517 | self.assertReportedFreshness('minimal', None, None, | ||
1184 | 518 | '') | ||
1185 | 519 | |||
1186 | 520 | def test_verbosity_short_out_of_date(self): | ||
1187 | 521 | self.assertReportedFreshness('short', '1.0-1ubuntu2', '1.0-1ubuntu1', | ||
1188 | 522 | '1.0-1ubuntu1 is OUT-OF-DATE,' | ||
1189 | 523 | ' Ubuntu Natty has 1.0-1ubuntu2\n') | ||
1190 | 524 | |||
1191 | 525 | def test_verbosity_short_up_to_date(self): | ||
1192 | 526 | self.assertReportedFreshness('short', '1.0-1ubuntu2', '1.0-1ubuntu2', | ||
1193 | 527 | '1.0-1ubuntu2 is CURRENT in Ubuntu Natty') | ||
1194 | 528 | |||
1195 | 529 | def test_verbosity_short_missing(self): | ||
1196 | 530 | self.assertReportedFreshness('short', None, None, | ||
1197 | 531 | 'Ubuntu Natty is MISSING a version') | ||
1198 | 532 | |||
1199 | 533 | def test_verbosity_all_no_tags(self): | ||
1200 | 534 | self.assertReportedFreshness('all', '1.0-1ubuntu2', None, | ||
1201 | 535 | 'Most recent Ubuntu Natty version: 1.0-1ubuntu2\n' | ||
1202 | 536 | 'Packaging branch version: None\n' | ||
1203 | 537 | 'Packaging branch status: OUT-OF-DATE\n') | ||
1204 | 538 | |||
1205 | 539 | def test_verbosity_all_out_of_date(self): | ||
1206 | 540 | self.assertReportedFreshness('all', '1.0-1ubuntu2', '1.0-1ubuntu1', | ||
1207 | 541 | 'Most recent Ubuntu Natty version: 1.0-1ubuntu2\n' | ||
1208 | 542 | 'Packaging branch version: 1.0-1ubuntu1\n' | ||
1209 | 543 | 'Packaging branch status: OUT-OF-DATE\n') | ||
1210 | 544 | |||
1211 | 545 | def test_verbosity_all_up_to_date(self): | ||
1212 | 546 | self.assertReportedFreshness('all', '1.0-1ubuntu2', '1.0-1ubuntu2', | ||
1213 | 547 | 'Most recent Ubuntu Natty version: 1.0-1ubuntu2\n' | ||
1214 | 548 | 'Packaging branch status: CURRENT\n') | ||
1215 | 549 | |||
1216 | 550 | def test_verbosity_all_missing(self): | ||
1217 | 551 | self.assertReportedFreshness('all', None, None, | ||
1218 | 552 | 'Most recent Ubuntu Natty version: MISSING\n') | ||
1219 | 553 | |||
1220 | 554 | def test_verbosity_None_is_all(self): | ||
1221 | 555 | self.assertReportedFreshness(None, '1.0-1ubuntu2', '1.0-1ubuntu2', | ||
1222 | 556 | 'Most recent Ubuntu Natty version: 1.0-1ubuntu2\n' | ||
1223 | 557 | 'Packaging branch status: CURRENT\n') | ||
1224 | 0 | 558 | ||
1225 | === modified file 'bzrlib/repofmt/groupcompress_repo.py' | |||
1226 | --- bzrlib/repofmt/groupcompress_repo.py 2010-08-07 00:54:52 +0000 | |||
1227 | +++ bzrlib/repofmt/groupcompress_repo.py 2011-09-27 11:59:22 +0000 | |||
1228 | @@ -1,4 +1,4 @@ | |||
1230 | 1 | # Copyright (C) 2008, 2009, 2010 Canonical Ltd | 1 | # Copyright (C) 2008-2011 Canonical Ltd |
1231 | 2 | # | 2 | # |
1232 | 3 | # This program is free software; you can redistribute it and/or modify | 3 | # This program is free software; you can redistribute it and/or modify |
1233 | 4 | # it under the terms of the GNU General Public License as published by | 4 | # it under the terms of the GNU General Public License as published by |
1234 | @@ -419,9 +419,18 @@ | |||
1235 | 419 | inventory_keys = source_vf.keys() | 419 | inventory_keys = source_vf.keys() |
1236 | 420 | missing_inventories = set(self.revision_keys).difference(inventory_keys) | 420 | missing_inventories = set(self.revision_keys).difference(inventory_keys) |
1237 | 421 | if missing_inventories: | 421 | if missing_inventories: |
1241 | 422 | missing_inventories = sorted(missing_inventories) | 422 | # Go back to the original repo, to see if these are really missing |
1242 | 423 | raise ValueError('We are missing inventories for revisions: %s' | 423 | # https://bugs.launchpad.net/bzr/+bug/437003 |
1243 | 424 | % (missing_inventories,)) | 424 | # If we are packing a subset of the repo, it is fine to just have |
1244 | 425 | # the data in another Pack file, which is not included in this pack | ||
1245 | 426 | # operation. | ||
1246 | 427 | inv_index = self._pack_collection.repo.inventories._index | ||
1247 | 428 | pmap = inv_index.get_parent_map(missing_inventories) | ||
1248 | 429 | really_missing = missing_inventories.difference(pmap) | ||
1249 | 430 | if really_missing: | ||
1250 | 431 | missing_inventories = sorted(really_missing) | ||
1251 | 432 | raise ValueError('We are missing inventories for revisions: %s' | ||
1252 | 433 | % (missing_inventories,)) | ||
1253 | 425 | self._copy_stream(source_vf, target_vf, inventory_keys, | 434 | self._copy_stream(source_vf, target_vf, inventory_keys, |
1254 | 426 | 'inventories', self._get_filtered_inv_stream, 2) | 435 | 'inventories', self._get_filtered_inv_stream, 2) |
1255 | 427 | 436 | ||
1256 | 428 | 437 | ||
1257 | === modified file 'bzrlib/tests/per_repository_reference/__init__.py' | |||
1258 | --- bzrlib/tests/per_repository_reference/__init__.py 2010-08-07 00:54:52 +0000 | |||
1259 | +++ bzrlib/tests/per_repository_reference/__init__.py 2011-09-27 11:59:22 +0000 | |||
1260 | @@ -117,6 +117,7 @@ | |||
1261 | 117 | 'bzrlib.tests.per_repository_reference.test_fetch', | 117 | 'bzrlib.tests.per_repository_reference.test_fetch', |
1262 | 118 | 'bzrlib.tests.per_repository_reference.test_get_record_stream', | 118 | 'bzrlib.tests.per_repository_reference.test_get_record_stream', |
1263 | 119 | 'bzrlib.tests.per_repository_reference.test_get_rev_id_for_revno', | 119 | 'bzrlib.tests.per_repository_reference.test_get_rev_id_for_revno', |
1264 | 120 | 'bzrlib.tests.per_repository_reference.test_graph', | ||
1265 | 120 | 'bzrlib.tests.per_repository_reference.test_initialize', | 121 | 'bzrlib.tests.per_repository_reference.test_initialize', |
1266 | 121 | 'bzrlib.tests.per_repository_reference.test_unlock', | 122 | 'bzrlib.tests.per_repository_reference.test_unlock', |
1267 | 122 | ] | 123 | ] |
1268 | 123 | 124 | ||
1269 | === added file 'bzrlib/tests/per_repository_reference/test_graph.py' | |||
1270 | --- bzrlib/tests/per_repository_reference/test_graph.py 1970-01-01 00:00:00 +0000 | |||
1271 | +++ bzrlib/tests/per_repository_reference/test_graph.py 2011-09-27 11:59:22 +0000 | |||
1272 | @@ -0,0 +1,45 @@ | |||
1273 | 1 | # Copyright (C) 2011 Canonical Ltd | ||
1274 | 2 | # | ||
1275 | 3 | # This program is free software; you can redistribute it and/or modify | ||
1276 | 4 | # it under the terms of the GNU General Public License as published by | ||
1277 | 5 | # the Free Software Foundation; either version 2 of the License, or | ||
1278 | 6 | # (at your option) any later version. | ||
1279 | 7 | # | ||
1280 | 8 | # This program is distributed in the hope that it will be useful, | ||
1281 | 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1282 | 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1283 | 11 | # GNU General Public License for more details. | ||
1284 | 12 | # | ||
1285 | 13 | # You should have received a copy of the GNU General Public License | ||
1286 | 14 | # along with this program; if not, write to the Free Software | ||
1287 | 15 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
1288 | 16 | |||
1289 | 17 | |||
1290 | 18 | """Tests for graph operations on stacked repositories.""" | ||
1291 | 19 | |||
1292 | 20 | |||
1293 | 21 | from bzrlib.tests.per_repository import TestCaseWithRepository | ||
1294 | 22 | |||
1295 | 23 | |||
1296 | 24 | class TestGraph(TestCaseWithRepository): | ||
1297 | 25 | |||
1298 | 26 | def test_get_known_graph_ancestry_stacked(self): | ||
1299 | 27 | """get_known_graph_ancestry works correctly on stacking. | ||
1300 | 28 | |||
1301 | 29 | See <https://bugs.launchpad.net/bugs/715000>. | ||
1302 | 30 | """ | ||
1303 | 31 | branch_a, branch_b, branch_c, revid_1 = self.make_double_stacked_branches() | ||
1304 | 32 | for br in [branch_c]: | ||
1305 | 33 | self.assertEquals( | ||
1306 | 34 | [revid_1], | ||
1307 | 35 | br.repository.get_known_graph_ancestry([revid_1]).topo_sort()) | ||
1308 | 36 | |||
1309 | 37 | def make_double_stacked_branches(self): | ||
1310 | 38 | wt_a = self.make_branch_and_tree('a') | ||
1311 | 39 | branch_a = wt_a.branch | ||
1312 | 40 | branch_b = self.make_branch('b') | ||
1313 | 41 | branch_b.set_stacked_on_url('../a') | ||
1314 | 42 | branch_c = self.make_branch('c') | ||
1315 | 43 | branch_c.set_stacked_on_url('../b') | ||
1316 | 44 | revid_1 = wt_a.commit('first commit') | ||
1317 | 45 | return branch_a, branch_b, branch_c, revid_1 | ||
1318 | 0 | 46 | ||
1319 | === modified file 'bzrlib/tests/test_config.py' | |||
1320 | --- bzrlib/tests/test_config.py 2010-08-07 00:54:52 +0000 | |||
1321 | +++ bzrlib/tests/test_config.py 2011-09-27 11:59:22 +0000 | |||
1322 | @@ -241,11 +241,37 @@ | |||
1323 | 241 | """ | 241 | """ |
1324 | 242 | co = config.ConfigObj() | 242 | co = config.ConfigObj() |
1325 | 243 | co['test'] = 'foo#bar' | 243 | co['test'] = 'foo#bar' |
1327 | 244 | lines = co.write() | 244 | outfile = StringIO() |
1328 | 245 | co.write(outfile=outfile) | ||
1329 | 246 | lines = outfile.getvalue().splitlines() | ||
1330 | 245 | self.assertEqual(lines, ['test = "foo#bar"']) | 247 | self.assertEqual(lines, ['test = "foo#bar"']) |
1331 | 246 | co2 = config.ConfigObj(lines) | 248 | co2 = config.ConfigObj(lines) |
1332 | 247 | self.assertEqual(co2['test'], 'foo#bar') | 249 | self.assertEqual(co2['test'], 'foo#bar') |
1333 | 248 | 250 | ||
1334 | 251 | def test_triple_quotes(self): | ||
1335 | 252 | # Bug #710410: if the value string has triple quotes | ||
1336 | 253 | # then ConfigObj versions up to 4.7.2 will quote them wrong | ||
1337 | 254 | # and won't able to read them back | ||
1338 | 255 | triple_quotes_value = '''spam | ||
1339 | 256 | """ that's my spam """ | ||
1340 | 257 | eggs''' | ||
1341 | 258 | co = config.ConfigObj() | ||
1342 | 259 | co['test'] = triple_quotes_value | ||
1343 | 260 | # While writing this test another bug in ConfigObj has been found: | ||
1344 | 261 | # method co.write() without arguments produces list of lines | ||
1345 | 262 | # one option per line, and multiline values are not split | ||
1346 | 263 | # across multiple lines, | ||
1347 | 264 | # and that breaks the parsing these lines back by ConfigObj. | ||
1348 | 265 | # This issue only affects test, but it's better to avoid | ||
1349 | 266 | # `co.write()` construct at all. | ||
1350 | 267 | # [bialix 20110222] bug report sent to ConfigObj's author | ||
1351 | 268 | outfile = StringIO() | ||
1352 | 269 | co.write(outfile=outfile) | ||
1353 | 270 | output = outfile.getvalue() | ||
1354 | 271 | # now we're trying to read it back | ||
1355 | 272 | co2 = config.ConfigObj(StringIO(output)) | ||
1356 | 273 | self.assertEquals(triple_quotes_value, co2['test']) | ||
1357 | 274 | |||
1358 | 249 | 275 | ||
1359 | 250 | erroneous_config = """[section] # line 1 | 276 | erroneous_config = """[section] # line 1 |
1360 | 251 | good=good # line 2 | 277 | good=good # line 2 |
1361 | 252 | 278 | ||
1362 | === modified file 'bzrlib/tests/test_conflicts.py' | |||
1363 | --- bzrlib/tests/test_conflicts.py 2011-04-07 15:30:17 +0000 | |||
1364 | +++ bzrlib/tests/test_conflicts.py 2011-09-27 11:59:22 +0000 | |||
1365 | @@ -1049,6 +1049,70 @@ | |||
1366 | 1049 | """) | 1049 | """) |
1367 | 1050 | 1050 | ||
1368 | 1051 | 1051 | ||
1369 | 1052 | class TestNoFinalPath(script.TestCaseWithTransportAndScript): | ||
1370 | 1053 | |||
1371 | 1054 | def test_bug_805809(self): | ||
1372 | 1055 | self.run_script(""" | ||
1373 | 1056 | $ bzr init trunk | ||
1374 | 1057 | Created a standalone tree (format: 2a) | ||
1375 | 1058 | $ cd trunk | ||
1376 | 1059 | $ echo trunk >file | ||
1377 | 1060 | $ bzr add | ||
1378 | 1061 | adding file | ||
1379 | 1062 | $ bzr commit -m 'create file on trunk' | ||
1380 | 1063 | 2>Committing to: .../trunk/ | ||
1381 | 1064 | 2>added file | ||
1382 | 1065 | 2>Committed revision 1. | ||
1383 | 1066 | # Create a debian branch based on trunk | ||
1384 | 1067 | $ cd .. | ||
1385 | 1068 | $ bzr branch trunk -r 1 debian | ||
1386 | 1069 | 2>Branched 1 revision(s). | ||
1387 | 1070 | $ cd debian | ||
1388 | 1071 | $ mkdir dir | ||
1389 | 1072 | $ bzr add | ||
1390 | 1073 | adding dir | ||
1391 | 1074 | $ bzr mv file dir | ||
1392 | 1075 | file => dir/file | ||
1393 | 1076 | $ bzr commit -m 'rename file to dir/file for debian' | ||
1394 | 1077 | 2>Committing to: .../debian/ | ||
1395 | 1078 | 2>added dir | ||
1396 | 1079 | 2>renamed file => dir/file | ||
1397 | 1080 | 2>Committed revision 2. | ||
1398 | 1081 | # Create an experimental branch with a new root-id | ||
1399 | 1082 | $ cd .. | ||
1400 | 1083 | $ bzr init experimental | ||
1401 | 1084 | $ cd experimental | ||
1402 | 1085 | # merge debian even without a common ancestor | ||
1403 | 1086 | $ bzr merge ../debian -r0..2 | ||
1404 | 1087 | 2>+N dir/ | ||
1405 | 1088 | 2>+N dir/file | ||
1406 | 1089 | 2>All changes applied successfully. | ||
1407 | 1090 | $ bzr commit -m 'merging debian into experimental' | ||
1408 | 1091 | 2>Committing to: .../experimental/ | ||
1409 | 1092 | 2>deleted | ||
1410 | 1093 | 2>modified dir | ||
1411 | 1094 | 2>Committed revision 1. | ||
1412 | 1095 | # Create an ubuntu branch with yet another root-id | ||
1413 | 1096 | $ cd .. | ||
1414 | 1097 | $ bzr init ubuntu | ||
1415 | 1098 | $ cd ubuntu | ||
1416 | 1099 | # Also merge debian | ||
1417 | 1100 | $ bzr merge ../debian -r0..2 | ||
1418 | 1101 | 2>+N dir/ | ||
1419 | 1102 | 2>+N dir/file | ||
1420 | 1103 | 2>All changes applied successfully. | ||
1421 | 1104 | $ bzr commit -m 'merging debian' | ||
1422 | 1105 | 2>Committing to: .../ubuntu/ | ||
1423 | 1106 | 2>deleted | ||
1424 | 1107 | 2>modified dir | ||
1425 | 1108 | 2>Committed revision 1. | ||
1426 | 1109 | # Now try to merge experimental | ||
1427 | 1110 | $ bzr merge ../experimental | ||
1428 | 1111 | 2>Path conflict: dir / dir | ||
1429 | 1112 | 2>1 conflicts encountered. | ||
1430 | 1113 | """) | ||
1431 | 1114 | |||
1432 | 1115 | |||
1433 | 1052 | class TestResolveActionOption(tests.TestCase): | 1116 | class TestResolveActionOption(tests.TestCase): |
1434 | 1053 | 1117 | ||
1435 | 1054 | def setUp(self): | 1118 | def setUp(self): |
1436 | 1055 | 1119 | ||
1437 | === modified file 'bzrlib/tests/test_repository.py' | |||
1438 | --- bzrlib/tests/test_repository.py 2010-08-07 00:54:52 +0000 | |||
1439 | +++ bzrlib/tests/test_repository.py 2011-09-27 11:59:22 +0000 | |||
1440 | @@ -1,4 +1,4 @@ | |||
1442 | 1 | # Copyright (C) 2006-2010 Canonical Ltd | 1 | # Copyright (C) 2006-2011 Canonical Ltd |
1443 | 2 | # | 2 | # |
1444 | 3 | # This program is free software; you can redistribute it and/or modify | 3 | # This program is free software; you can redistribute it and/or modify |
1445 | 4 | # it under the terms of the GNU General Public License as published by | 4 | # it under the terms of the GNU General Public License as published by |
1446 | @@ -1622,6 +1622,106 @@ | |||
1447 | 1622 | self.assertTrue(new_pack.signature_index._optimize_for_size) | 1622 | self.assertTrue(new_pack.signature_index._optimize_for_size) |
1448 | 1623 | 1623 | ||
1449 | 1624 | 1624 | ||
1450 | 1625 | class TestGCCHKPacker(TestCaseWithTransport): | ||
1451 | 1626 | |||
1452 | 1627 | def make_abc_branch(self): | ||
1453 | 1628 | builder = self.make_branch_builder('source') | ||
1454 | 1629 | builder.start_series() | ||
1455 | 1630 | builder.build_snapshot('A', None, [ | ||
1456 | 1631 | ('add', ('', 'root-id', 'directory', None)), | ||
1457 | 1632 | ('add', ('file', 'file-id', 'file', 'content\n')), | ||
1458 | 1633 | ]) | ||
1459 | 1634 | builder.build_snapshot('B', ['A'], [ | ||
1460 | 1635 | ('add', ('dir', 'dir-id', 'directory', None))]) | ||
1461 | 1636 | builder.build_snapshot('C', ['B'], [ | ||
1462 | 1637 | ('modify', ('file-id', 'new content\n'))]) | ||
1463 | 1638 | builder.finish_series() | ||
1464 | 1639 | return builder.get_branch() | ||
1465 | 1640 | |||
1466 | 1641 | def make_branch_with_disjoint_inventory_and_revision(self): | ||
1467 | 1642 | """a repo with separate packs for a revisions Revision and Inventory. | ||
1468 | 1643 | |||
1469 | 1644 | There will be one pack file that holds the Revision content, and one | ||
1470 | 1645 | for the Inventory content. | ||
1471 | 1646 | |||
1472 | 1647 | :return: (repository, | ||
1473 | 1648 | pack_name_with_rev_A_Revision, | ||
1474 | 1649 | pack_name_with_rev_A_Inventory, | ||
1475 | 1650 | pack_name_with_rev_C_content) | ||
1476 | 1651 | """ | ||
1477 | 1652 | b_source = self.make_abc_branch() | ||
1478 | 1653 | b_base = b_source.bzrdir.sprout('base', revision_id='A').open_branch() | ||
1479 | 1654 | b_stacked = b_base.bzrdir.sprout('stacked', stacked=True).open_branch() | ||
1480 | 1655 | b_stacked.lock_write() | ||
1481 | 1656 | self.addCleanup(b_stacked.unlock) | ||
1482 | 1657 | b_stacked.fetch(b_source, 'B') | ||
1483 | 1658 | # Now re-open the stacked repo directly (no fallbacks) so that we can | ||
1484 | 1659 | # fill in the A rev. | ||
1485 | 1660 | repo_not_stacked = b_stacked.bzrdir.open_repository() | ||
1486 | 1661 | repo_not_stacked.lock_write() | ||
1487 | 1662 | self.addCleanup(repo_not_stacked.unlock) | ||
1488 | 1663 | # Now we should have a pack file with A's inventory, but not its | ||
1489 | 1664 | # Revision | ||
1490 | 1665 | self.assertEqual([('A',), ('B',)], | ||
1491 | 1666 | sorted(repo_not_stacked.inventories.keys())) | ||
1492 | 1667 | self.assertEqual([('B',)], | ||
1493 | 1668 | sorted(repo_not_stacked.revisions.keys())) | ||
1494 | 1669 | stacked_pack_names = repo_not_stacked._pack_collection.names() | ||
1495 | 1670 | # We have a couple names here, figure out which has A's inventory | ||
1496 | 1671 | for name in stacked_pack_names: | ||
1497 | 1672 | pack = repo_not_stacked._pack_collection.get_pack_by_name(name) | ||
1498 | 1673 | keys = [n[1] for n in pack.inventory_index.iter_all_entries()] | ||
1499 | 1674 | if ('A',) in keys: | ||
1500 | 1675 | inv_a_pack_name = name | ||
1501 | 1676 | break | ||
1502 | 1677 | else: | ||
1503 | 1678 | self.fail('Could not find pack containing A\'s inventory') | ||
1504 | 1679 | repo_not_stacked.fetch(b_source.repository, 'A') | ||
1505 | 1680 | self.assertEqual([('A',), ('B',)], | ||
1506 | 1681 | sorted(repo_not_stacked.revisions.keys())) | ||
1507 | 1682 | new_pack_names = set(repo_not_stacked._pack_collection.names()) | ||
1508 | 1683 | rev_a_pack_names = new_pack_names.difference(stacked_pack_names) | ||
1509 | 1684 | self.assertEqual(1, len(rev_a_pack_names)) | ||
1510 | 1685 | rev_a_pack_name = list(rev_a_pack_names)[0] | ||
1511 | 1686 | # Now fetch 'C', so we have a couple pack files to join | ||
1512 | 1687 | repo_not_stacked.fetch(b_source.repository, 'C') | ||
1513 | 1688 | rev_c_pack_names = set(repo_not_stacked._pack_collection.names()) | ||
1514 | 1689 | rev_c_pack_names = rev_c_pack_names.difference(new_pack_names) | ||
1515 | 1690 | self.assertEqual(1, len(rev_c_pack_names)) | ||
1516 | 1691 | rev_c_pack_name = list(rev_c_pack_names)[0] | ||
1517 | 1692 | return (repo_not_stacked, rev_a_pack_name, inv_a_pack_name, | ||
1518 | 1693 | rev_c_pack_name) | ||
1519 | 1694 | |||
1520 | 1695 | def test_pack_with_distant_inventories(self): | ||
1521 | 1696 | # See https://bugs.launchpad.net/bzr/+bug/437003 | ||
1522 | 1697 | # When repacking, it is possible to have an inventory in a different | ||
1523 | 1698 | # pack file than the associated revision. An autopack can then come | ||
1524 | 1699 | # along, and miss that inventory, and complain. | ||
1525 | 1700 | (repo, rev_a_pack_name, inv_a_pack_name, rev_c_pack_name | ||
1526 | 1701 | ) = self.make_branch_with_disjoint_inventory_and_revision() | ||
1527 | 1702 | a_pack = repo._pack_collection.get_pack_by_name(rev_a_pack_name) | ||
1528 | 1703 | c_pack = repo._pack_collection.get_pack_by_name(rev_c_pack_name) | ||
1529 | 1704 | packer = groupcompress_repo.GCCHKPacker(repo._pack_collection, | ||
1530 | 1705 | [a_pack, c_pack], '.test-pack') | ||
1531 | 1706 | # This would raise ValueError in bug #437003, but should not raise an | ||
1532 | 1707 | # error once fixed. | ||
1533 | 1708 | packer.pack() | ||
1534 | 1709 | |||
1535 | 1710 | def test_pack_with_missing_inventory(self): | ||
1536 | 1711 | # Similar to test_pack_with_missing_inventory, but this time, we force | ||
1537 | 1712 | # the A inventory to actually be gone from the repository. | ||
1538 | 1713 | (repo, rev_a_pack_name, inv_a_pack_name, rev_c_pack_name | ||
1539 | 1714 | ) = self.make_branch_with_disjoint_inventory_and_revision() | ||
1540 | 1715 | inv_a_pack = repo._pack_collection.get_pack_by_name(inv_a_pack_name) | ||
1541 | 1716 | repo._pack_collection._remove_pack_from_memory(inv_a_pack) | ||
1542 | 1717 | packer = groupcompress_repo.GCCHKPacker(repo._pack_collection, | ||
1543 | 1718 | repo._pack_collection.all_packs(), '.test-pack') | ||
1544 | 1719 | e = self.assertRaises(ValueError, packer.pack) | ||
1545 | 1720 | packer.new_pack.abort() | ||
1546 | 1721 | self.assertContainsRe(str(e), | ||
1547 | 1722 | r"We are missing inventories for revisions: .*'A'") | ||
1548 | 1723 | |||
1549 | 1724 | |||
1550 | 1625 | class TestCrossFormatPacks(TestCaseWithTransport): | 1725 | class TestCrossFormatPacks(TestCaseWithTransport): |
1551 | 1626 | 1726 | ||
1552 | 1627 | def log_pack(self, hint=None): | 1727 | def log_pack(self, hint=None): |
1553 | 1628 | 1728 | ||
1554 | === modified file 'bzrlib/transport/http/_pycurl.py' | |||
1555 | --- bzrlib/transport/http/_pycurl.py 2011-04-07 15:30:17 +0000 | |||
1556 | +++ bzrlib/transport/http/_pycurl.py 2011-09-27 11:59:22 +0000 | |||
1557 | @@ -305,19 +305,19 @@ | |||
1558 | 305 | url, 'Unable to handle http code %d%s' % (code,msg)) | 305 | url, 'Unable to handle http code %d%s' % (code,msg)) |
1559 | 306 | 306 | ||
1560 | 307 | def _debug_cb(self, kind, text): | 307 | def _debug_cb(self, kind, text): |
1563 | 308 | if kind in (pycurl.INFOTYPE_HEADER_IN, pycurl.INFOTYPE_DATA_IN, | 308 | if kind in (pycurl.INFOTYPE_HEADER_IN, pycurl.INFOTYPE_DATA_IN): |
1562 | 309 | pycurl.INFOTYPE_SSL_DATA_IN): | ||
1564 | 310 | self._report_activity(len(text), 'read') | 309 | self._report_activity(len(text), 'read') |
1565 | 311 | if (kind == pycurl.INFOTYPE_HEADER_IN | 310 | if (kind == pycurl.INFOTYPE_HEADER_IN |
1566 | 312 | and 'http' in debug.debug_flags): | 311 | and 'http' in debug.debug_flags): |
1567 | 313 | mutter('< %s' % text) | 312 | mutter('< %s' % text) |
1570 | 314 | elif kind in (pycurl.INFOTYPE_HEADER_OUT, pycurl.INFOTYPE_DATA_OUT, | 313 | elif kind in (pycurl.INFOTYPE_HEADER_OUT, pycurl.INFOTYPE_DATA_OUT): |
1569 | 315 | pycurl.INFOTYPE_SSL_DATA_OUT): | ||
1571 | 316 | self._report_activity(len(text), 'write') | 314 | self._report_activity(len(text), 'write') |
1572 | 317 | if (kind == pycurl.INFOTYPE_HEADER_OUT | 315 | if (kind == pycurl.INFOTYPE_HEADER_OUT |
1573 | 318 | and 'http' in debug.debug_flags): | 316 | and 'http' in debug.debug_flags): |
1574 | 319 | mutter('> %s' % text) | 317 | mutter('> %s' % text) |
1576 | 320 | elif kind == pycurl.INFOTYPE_TEXT and 'http' in debug.debug_flags: | 318 | elif (kind in (pycurl.INFOTYPE_TEXT, pycurl.INFOTYPE_SSL_DATA_IN, |
1577 | 319 | pycurl.INFOTYPE_SSL_DATA_OUT) | ||
1578 | 320 | and 'http' in debug.debug_flags): | ||
1579 | 321 | mutter('* %s' % text) | 321 | mutter('* %s' % text) |
1580 | 322 | 322 | ||
1581 | 323 | def _set_curl_options(self, curl): | 323 | def _set_curl_options(self, curl): |
1582 | 324 | 324 | ||
1583 | === modified file 'bzrlib/util/configobj/configobj.py' | |||
1584 | --- bzrlib/util/configobj/configobj.py 2009-05-16 13:51:08 +0000 | |||
1585 | +++ bzrlib/util/configobj/configobj.py 2011-09-27 11:59:22 +0000 | |||
1586 | @@ -1794,10 +1794,12 @@ | |||
1587 | 1794 | def _get_triple_quote(self, value): | 1794 | def _get_triple_quote(self, value): |
1588 | 1795 | if (value.find('"""') != -1) and (value.find("'''") != -1): | 1795 | if (value.find('"""') != -1) and (value.find("'''") != -1): |
1589 | 1796 | raise ConfigObjError('Value "%s" cannot be safely quoted.' % value) | 1796 | raise ConfigObjError('Value "%s" cannot be safely quoted.' % value) |
1590 | 1797 | # upstream version (up to version 4.7.2) has the bug with incorrect quoting; | ||
1591 | 1798 | # fixed in our copy based on the suggestion of ConfigObj's author | ||
1592 | 1797 | if value.find('"""') == -1: | 1799 | if value.find('"""') == -1: |
1593 | 1800 | quot = tsquot | ||
1594 | 1801 | else: | ||
1595 | 1798 | quot = tdquot | 1802 | quot = tdquot |
1596 | 1799 | else: | ||
1597 | 1800 | quot = tsquot | ||
1598 | 1801 | return quot | 1803 | return quot |
1599 | 1802 | 1804 | ||
1600 | 1803 | 1805 | ||
1601 | 1804 | 1806 | ||
1602 | === modified file 'bzrlib/versionedfile.py' | |||
1603 | --- bzrlib/versionedfile.py 2010-08-07 00:54:52 +0000 | |||
1604 | +++ bzrlib/versionedfile.py 2011-09-27 11:59:22 +0000 | |||
1605 | @@ -1090,6 +1090,19 @@ | |||
1606 | 1090 | def _extract_blocks(self, version_id, source, target): | 1090 | def _extract_blocks(self, version_id, source, target): |
1607 | 1091 | return None | 1091 | return None |
1608 | 1092 | 1092 | ||
1609 | 1093 | def _transitive_fallbacks(self): | ||
1610 | 1094 | """Return the whole stack of fallback versionedfiles. | ||
1611 | 1095 | |||
1612 | 1096 | This VersionedFiles may have a list of fallbacks, but it doesn't | ||
1613 | 1097 | necessarily know about the whole stack going down, and it can't know | ||
1614 | 1098 | at open time because they may change after the objects are opened. | ||
1615 | 1099 | """ | ||
1616 | 1100 | all_fallbacks = [] | ||
1617 | 1101 | for a_vfs in self._fallback_vfs: | ||
1618 | 1102 | all_fallbacks.append(a_vfs) | ||
1619 | 1103 | all_fallbacks.extend(a_vfs._transitive_fallbacks()) | ||
1620 | 1104 | return all_fallbacks | ||
1621 | 1105 | |||
1622 | 1093 | 1106 | ||
1623 | 1094 | class ThunkedVersionedFiles(VersionedFiles): | 1107 | class ThunkedVersionedFiles(VersionedFiles): |
1624 | 1095 | """Storage for many versioned files thunked onto a 'VersionedFile' class. | 1108 | """Storage for many versioned files thunked onto a 'VersionedFile' class. |
1625 | 1096 | 1109 | ||
1626 | === modified file 'debian/changelog' | |||
1627 | --- debian/changelog 2011-04-07 15:30:17 +0000 | |||
1628 | +++ debian/changelog 2011-09-27 11:59:22 +0000 | |||
1629 | @@ -1,3 +1,13 @@ | |||
1630 | 1 | bzr (2.2.5-0ubuntu1) maverick-proposed; urgency=low | ||
1631 | 2 | |||
1632 | 3 | * New upstream release. | ||
1633 | 4 | + Fixes merge failing with NoFinalPath. LP: #805809 | ||
1634 | 5 | + Warns users when brancing from UDD branches that are out of date. | ||
1635 | 6 | LP: #609187 | ||
1636 | 7 | + stacking is now fully transitive. LP: #715000 | ||
1637 | 8 | |||
1638 | 9 | -- Jelmer Vernooij <jelmer@ubuntu.com> Tue, 27 Sep 2011 10:30:17 +0200 | ||
1639 | 10 | |||
1640 | 1 | bzr (2.2.4-0ubuntu1) maverick-proposed; urgency=low | 11 | bzr (2.2.4-0ubuntu1) maverick-proposed; urgency=low |
1641 | 2 | 12 | ||
1642 | 3 | [ Jelmer Vernooij ] | 13 | [ Jelmer Vernooij ] |
1643 | 4 | 14 | ||
1644 | === modified file 'debian/watch' | |||
1645 | --- debian/watch 2011-04-07 15:30:17 +0000 | |||
1646 | +++ debian/watch 2011-09-27 11:59:22 +0000 | |||
1647 | @@ -1,3 +1,3 @@ | |||
1648 | 1 | version=3 | 1 | version=3 |
1649 | 2 | opts="uversionmangle=s/rc/~rc/;s/b/~b/;s/^/2.2./" \ | 2 | opts="uversionmangle=s/rc/~rc/;s/b/~b/;s/^/2.2./" \ |
1651 | 3 | https://launchpad.net/bzr/+download http://launchpad.net/bzr/.*/bzr-2.2.(.+).tar.gz | 3 | https://launchpad.net/bzr/2.2 http://launchpad.net/bzr/.*/bzr-2.2.(.+).tar.gz |
1652 | 4 | 4 | ||
1653 | === modified file 'doc/en/whats-new/whats-new-in-2.2.txt' | |||
1654 | --- doc/en/whats-new/whats-new-in-2.2.txt 2011-04-07 15:30:17 +0000 | |||
1655 | +++ doc/en/whats-new/whats-new-in-2.2.txt 2011-09-27 11:59:22 +0000 | |||
1656 | @@ -36,7 +36,10 @@ | |||
1657 | 36 | server and python-2.7 compatibility. | 36 | server and python-2.7 compatibility. |
1658 | 37 | 37 | ||
1659 | 38 | Bazaar 2.2.4 fixed a regression for some interactions with the launchpad | 38 | Bazaar 2.2.4 fixed a regression for some interactions with the launchpad |
1661 | 39 | server . | 39 | server. |
1662 | 40 | |||
1663 | 41 | Bazaar 2.2.5 fixed a regression in some rare conflict resolutions and warns | ||
1664 | 42 | when branching an out-of-date ubuntu packaging branch. | ||
1665 | 40 | 43 | ||
1666 | 41 | See the :doc:`../release-notes/index` for details. | 44 | See the :doc:`../release-notes/index` for details. |
1667 | 42 | 45 | ||
1668 | 43 | 46 | ||
1669 | === modified file 'setup.py' | |||
1670 | --- setup.py 2011-04-07 15:30:17 +0000 | |||
1671 | +++ setup.py 2011-09-27 11:59:22 +0000 | |||
1672 | @@ -468,6 +468,12 @@ | |||
1673 | 468 | packages.append('sqlite3') | 468 | packages.append('sqlite3') |
1674 | 469 | 469 | ||
1675 | 470 | 470 | ||
1676 | 471 | def get_fastimport_py2exe_info(includes, excludes, packages): | ||
1677 | 472 | # This is the python-fastimport package, not to be confused with the | ||
1678 | 473 | # bzr-fastimport plugin. | ||
1679 | 474 | packages.append('fastimport') | ||
1680 | 475 | |||
1681 | 476 | |||
1682 | 471 | if 'bdist_wininst' in sys.argv: | 477 | if 'bdist_wininst' in sys.argv: |
1683 | 472 | def find_docs(): | 478 | def find_docs(): |
1684 | 473 | docs = [] | 479 | docs = [] |
1685 | @@ -660,6 +666,9 @@ | |||
1686 | 660 | if 'svn' in plugins: | 666 | if 'svn' in plugins: |
1687 | 661 | get_svn_py2exe_info(includes, excludes, packages) | 667 | get_svn_py2exe_info(includes, excludes, packages) |
1688 | 662 | 668 | ||
1689 | 669 | if 'fastimport' in plugins: | ||
1690 | 670 | get_fastimport_py2exe_info(includes, excludes, packages) | ||
1691 | 671 | |||
1692 | 663 | if "TBZR" in os.environ: | 672 | if "TBZR" in os.environ: |
1693 | 664 | # TORTOISE_OVERLAYS_MSI_WIN32 must be set to the location of the | 673 | # TORTOISE_OVERLAYS_MSI_WIN32 must be set to the location of the |
1694 | 665 | # TortoiseOverlays MSI installer file. It is in the TSVN svn repo and | 674 | # TortoiseOverlays MSI installer file. It is in the TSVN svn repo and |