Merge lp:~jelmer/bzr-builddeb/dep3-patch into lp:bzr-builddeb
- dep3-patch
- Merge into trunk
Proposed by
Jelmer Vernooij
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | James Westby | ||||
Approved revision: | 569 | ||||
Merged at revision: | 559 | ||||
Proposed branch: | lp:~jelmer/bzr-builddeb/dep3-patch | ||||
Merge into: | lp:bzr-builddeb | ||||
Diff against target: |
647 lines (+578/-2) 8 files modified
__init__.py (+1/-0) cmds.py (+70/-0) debian/changelog (+7/-0) dep3.py (+177/-0) tests/__init__.py (+1/-0) tests/blackbox/__init__.py (+1/-2) tests/blackbox/test_dep3.py (+104/-0) tests/test_dep3.py (+217/-0) |
||||
To merge this branch: | bzr merge lp:~jelmer/bzr-builddeb/dep3-patch | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
James Westby | Approve | ||
Review via email: mp+60320@code.launchpad.net |
Commit message
Description of the change
Add a 'bzr dep3-patch' command which can spit out a -p1 patch with a DEP-3 compliant
header, looking at bzr metadata to pre-fill-in as much of the metadata as possible.
Another neat thing to do would be to use this new infrastructure to export a
set of loom threads to a debian/patches directory, but I'll leave that for some
other time.
To post a comment you must log in.
Revision history for this message
James Westby (james-w) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file '__init__.py' |
2 | --- __init__.py 2011-02-19 20:43:49 +0000 |
3 | +++ __init__.py 2011-05-08 15:53:28 +0000 |
4 | @@ -37,6 +37,7 @@ |
5 | commands = { |
6 | "bd_do": [], |
7 | "builddeb": ["bd"], |
8 | + "dep3_patch": [], |
9 | "dh_make": ["dh_make"], |
10 | "import_dsc": [], |
11 | "import_upstream": [], |
12 | |
13 | === modified file 'cmds.py' |
14 | --- cmds.py 2011-04-29 22:32:59 +0000 |
15 | +++ cmds.py 2011-05-08 15:53:28 +0000 |
16 | @@ -1205,3 +1205,73 @@ |
17 | note('Package prepared in %s' |
18 | % urlutils.unescape_for_display(tree.basedir, |
19 | self.outf.encoding)) |
20 | + |
21 | + |
22 | +class cmd_dep3_patch(Command): |
23 | + """Format the changes in a branch as a DEP-3 patch. |
24 | + |
25 | + """ |
26 | + |
27 | + takes_args = ["location"] |
28 | + |
29 | + directory_opt = Option('directory', |
30 | + help='Packaging tree for which to generate patch.', |
31 | + short_name='d', type=unicode) |
32 | + |
33 | + no_upstream_check_opt = Option('no-upstream-check', |
34 | + help="Don't check whether patch has been merged upstream.") |
35 | + |
36 | + takes_options = [directory_opt, "revision", no_upstream_check_opt] |
37 | + |
38 | + def run(self, location, directory=".", revision=None, no_upstream_check=False): |
39 | + from bzrlib.plugins.builddeb.dep3 import ( |
40 | + determine_applied_upstream, |
41 | + determine_forwarded, |
42 | + describe_origin, |
43 | + gather_bugs_and_authors, |
44 | + write_dep3_patch, |
45 | + ) |
46 | + packaging_tree, packaging_branch = BzrDir.open_containing_tree_or_branch( |
47 | + directory)[:2] |
48 | + tree, branch = BzrDir.open_containing_tree_or_branch(location)[:2] |
49 | + branch.lock_read() |
50 | + try: |
51 | + if revision is not None and len(revision) >= 1: |
52 | + revision_id = revision[-1].as_revision_id(branch) |
53 | + else: |
54 | + revision_id = branch.last_revision() |
55 | + graph = branch.repository.get_graph(packaging_branch.repository) |
56 | + if revision is not None and len(revision) == 2: |
57 | + base_revid = revision[0].as_revision_id(branch) |
58 | + else: |
59 | + base_revid = graph.find_unique_lca(revision_id, |
60 | + packaging_branch.last_revision()) |
61 | + interesting_revision_ids = graph.find_unique_ancestors(revision_id, [base_revid]) |
62 | + if len(interesting_revision_ids) == 0: |
63 | + raise BzrCommandError("No unmerged revisions") |
64 | + (bugs, authors, last_update) = gather_bugs_and_authors(branch.repository, |
65 | + interesting_revision_ids) |
66 | + config = branch.get_config() |
67 | + description = config.get_user_option("description") |
68 | + if description is None and len(interesting_revision_ids) == 1: |
69 | + # if there's just one revision, use that revisions commits message |
70 | + rev = branch.repository.get_revision(iter(interesting_revision_ids).next()) |
71 | + description = rev.message |
72 | + origin = describe_origin(branch, revision_id) |
73 | + if packaging_tree is None: |
74 | + packaging_tree = packaging_branch.basis_tree() |
75 | + builddeb_config = debuild_config(packaging_tree, True) |
76 | + if not no_upstream_check and builddeb_config.upstream_branch: |
77 | + upstream_branch = Branch.open(builddeb_config.upstream_branch) |
78 | + applied_upstream = determine_applied_upstream(upstream_branch, branch, |
79 | + revision_id) |
80 | + forwarded = determine_forwarded(upstream_branch, branch, revision_id) |
81 | + else: |
82 | + applied_upstream = None |
83 | + forwarded = None |
84 | + write_dep3_patch(self.outf, branch, base_revid, |
85 | + revision_id, bugs=bugs, authors=authors, origin=origin, |
86 | + forwarded=forwarded, applied_upstream=applied_upstream, |
87 | + description=description, last_update=last_update) |
88 | + finally: |
89 | + branch.unlock() |
90 | |
91 | === modified file 'debian/changelog' |
92 | --- debian/changelog 2011-05-06 16:43:16 +0000 |
93 | +++ debian/changelog 2011-05-08 15:53:28 +0000 |
94 | @@ -1,3 +1,10 @@ |
95 | +bzr-builddeb (2.7.5) UNRELEASED; urgency=low |
96 | + |
97 | + * New 'bzr dep3-patch' subcommand that can generate DEP-3 compliant |
98 | + patches. LP: #460576 |
99 | + |
100 | + -- Jelmer Vernooij <jelmer@debian.org> Sun, 08 May 2011 17:45:14 +0200 |
101 | + |
102 | bzr-builddeb (2.7.4) unstable; urgency=low |
103 | |
104 | [ Jelmer Vernooij ] |
105 | |
106 | === added file 'dep3.py' |
107 | --- dep3.py 1970-01-01 00:00:00 +0000 |
108 | +++ dep3.py 2011-05-08 15:53:28 +0000 |
109 | @@ -0,0 +1,177 @@ |
110 | +# dep3.py -- DEP-3 compatible patch formatting |
111 | +# Copyright (C) 2011 Canonical Ltd. |
112 | +# |
113 | +# This file is part of bzr-builddeb. |
114 | +# |
115 | +# bzr-builddeb is free software; you can redistribute it and/or modify |
116 | +# it under the terms of the GNU General Public License as published by |
117 | +# the Free Software Foundation; either version 2 of the License, or |
118 | +# (at your option) any later version. |
119 | +# |
120 | +# bzr-builddeb is distributed in the hope that it will be useful, |
121 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
122 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
123 | +# GNU General Public License for more details. |
124 | +# |
125 | +# You should have received a copy of the GNU General Public License |
126 | +# along with bzr-builddeb; if not, write to the Free Software |
127 | +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
128 | +# |
129 | + |
130 | +"""DEP-3 style patch formatting.""" |
131 | + |
132 | +from bzrlib import diff |
133 | + |
134 | +import time |
135 | + |
136 | + |
137 | +def write_dep3_bug_line(f, bug_url, status): |
138 | + """Write a DEP-3 compatible line with a bug link. |
139 | + |
140 | + :param f: File-like object to write to |
141 | + :param bug_url: Bug URL |
142 | + :param status: Bug status (e.g. "fixed") |
143 | + """ |
144 | + # For the moment, we only care about fixed bugs |
145 | + if status != "fixed": |
146 | + return |
147 | + if bug_url.startswith("http://bugs.debian.org/"): |
148 | + f.write("Bug-Debian: %s\n" % bug_url) |
149 | + else: |
150 | + # FIXME: Filter out Ubuntu bugs on Launchpad |
151 | + f.write("Bug: %s\n" % bug_url) |
152 | + |
153 | + |
154 | +def write_dep3_patch_header(f, description=None, origin=None, forwarded=None, |
155 | + bugs=None, authors=None, revision_id=None, last_update=None, |
156 | + applied_upstream=None): |
157 | + """Write a DEP3 patch header. |
158 | + |
159 | + :param f: File-like object to write to |
160 | + :param description: Description of the patch |
161 | + :param origin: Single line describing the origin of the patch |
162 | + :param forwarded: Single line describing whether and how the patch was |
163 | + forwarded |
164 | + :param bugs: Set of bugs fixed in this patch |
165 | + :param authors: Authors of the patch |
166 | + :param revision_id: Relevant bzr revision id |
167 | + :param last_update: Last update timestamp |
168 | + :param applied_upstream: If the patch is applied upstream, |
169 | + an informal string describing where it was merged |
170 | + """ |
171 | + # FIXME: Handle line breaks, etc sensibly |
172 | + if description is not None: |
173 | + description = description.strip("\n") |
174 | + description = description.replace("\n\n", "\n.\n") |
175 | + description = description.replace("\n", "\n ") |
176 | + f.write("Description: %s\n" % description) |
177 | + if origin is not None: |
178 | + f.write("Origin: %s\n" % origin) |
179 | + if forwarded is not None: |
180 | + f.write("Forwarded: %s\n" % forwarded) |
181 | + if authors is not None: |
182 | + for author in authors: |
183 | + f.write("Author: %s\n" % author) |
184 | + if bugs is not None: |
185 | + for bug_url, status in bugs: |
186 | + write_dep3_bug_line(f, bug_url, status) |
187 | + if last_update is not None: |
188 | + f.write("Last-Update: %s\n" % time.strftime("%Y-%m-%d", |
189 | + time.gmtime(last_update))) |
190 | + if applied_upstream is not None: |
191 | + f.write("Applied-Upstream: %s\n" % applied_upstream) |
192 | + if revision_id is not None: |
193 | + f.write("X-Bzr-Revision-Id: %s\n" % revision_id) |
194 | + f.write("\n") |
195 | + |
196 | + |
197 | +def gather_bugs_and_authors(repository, interesting_revision_ids): |
198 | + """Gather bug and author information from revisions. |
199 | + |
200 | + :param interesting_revision_ids: Iterable of revision ids to check |
201 | + :return: Tuple of bugs, authors and highest found commit timestamp |
202 | + """ |
203 | + authors = set() |
204 | + bugs = set() |
205 | + last_update = None |
206 | + for rev in repository.get_revisions(interesting_revision_ids): |
207 | + last_update = max(rev.timestamp, last_update) |
208 | + authors.update(rev.get_apparent_authors()) |
209 | + bugs.update(rev.iter_bugs()) |
210 | + return (bugs, authors, last_update) |
211 | + |
212 | + |
213 | +def determine_applied_upstream(upstream_branch, feature_branch, feature_revid=None): |
214 | + """Check if a particular revision has been merged upstream. |
215 | + |
216 | + :param upstream_branch: Upstream branch object |
217 | + :param feature_branch: Feature branch |
218 | + :param feature_revid: Revision id in feature branch to check, |
219 | + defaults to feature_branch tip. |
220 | + :return: String that can be used for Applied-Upstream field |
221 | + """ |
222 | + if feature_revid is None: |
223 | + feature_revid = feature_branch.last_revision() |
224 | + upstream_graph = feature_branch.repository.get_graph(upstream_branch.repository) |
225 | + merger = upstream_graph.find_lefthand_merger(feature_revid, |
226 | + upstream_branch.last_revision()) |
227 | + if merger is not None: |
228 | + return "merged in revision %s" % ( |
229 | + ".".join(str(x) for x in upstream_branch.revision_id_to_dotted_revno(merger)), ) |
230 | + else: |
231 | + return "no" |
232 | + |
233 | + |
234 | +def determine_forwarded(upstream_branch, feature_branch, feature_revid): |
235 | + """See if a branch has been forwarded to upstream. |
236 | + |
237 | + :param upstream_branch: Upstream branch object |
238 | + :param feature_branch: Feature branch |
239 | + :param feature_revid: Revision id in feature branch to check |
240 | + :return: String that can be used for Applied-Upstream field |
241 | + """ |
242 | + # FIXME: Check for Launchpad merge proposals from feature_branch (or its |
243 | + # public_branch) to upstream_branch |
244 | + |
245 | + # Are there any other ways to see that a patch has been forwarded upstream? |
246 | + return None |
247 | + |
248 | + |
249 | +def describe_origin(branch, revid): |
250 | + """Describe a tree for use in the origin field. |
251 | + |
252 | + :param branch: Branch to retrieve the revision from |
253 | + :param revid: Revision id |
254 | + """ |
255 | + public_branch_url = branch.get_public_branch() |
256 | + if public_branch_url is not None: |
257 | + return "commit, %s, revision: %s" % ( |
258 | + public_branch_url, |
259 | + ".".join(str(x) for x in branch.revision_id_to_dotted_revno(revid)), ) |
260 | + else: |
261 | + return "commit, revision id: %s" % revid |
262 | + |
263 | + |
264 | +def write_dep3_patch(f, branch, base_revid, target_revid, description=None, |
265 | + origin=None, forwarded=None, applied_upstream=None, bugs=None, |
266 | + authors=None, last_update=None): |
267 | + """Write a DEP-3 compliant patch. |
268 | + |
269 | + :param f: File-like object to write to |
270 | + :param repository: Repository to retrieve revisions from |
271 | + :param base_revid: Base revision id |
272 | + :param target_revid: Target revision id |
273 | + :param description: Optional description |
274 | + :param forwarded: Optional information on if/how the patch was forwarded |
275 | + :param applied_upstream: Optional information on how whether the patch |
276 | + was merged upstream |
277 | + :param bugs: Sequence of bug reports related to this patch |
278 | + :param authors: Sequence of authors of this patch |
279 | + :param last_update: Timestamp for last time this patch was updated |
280 | + """ |
281 | + write_dep3_patch_header(f, bugs=bugs, authors=authors, last_update=last_update, |
282 | + description=description, revision_id=target_revid, origin=origin, |
283 | + applied_upstream=applied_upstream, forwarded=forwarded) |
284 | + old_tree = branch.repository.revision_tree(base_revid) |
285 | + new_tree = branch.repository.revision_tree(target_revid) |
286 | + diff.show_diff_trees(old_tree, new_tree, f, old_label='old/', new_label='new/') |
287 | |
288 | === modified file 'tests/__init__.py' |
289 | --- tests/__init__.py 2011-02-11 13:35:53 +0000 |
290 | +++ tests/__init__.py 2011-05-08 15:53:28 +0000 |
291 | @@ -119,6 +119,7 @@ |
292 | 'test_bzrtools_import', |
293 | 'test_commit_message', |
294 | 'test_config', |
295 | + 'test_dep3', |
296 | 'test_dh_make', |
297 | 'test_hooks', |
298 | 'test_import_dsc', |
299 | |
300 | === modified file 'tests/blackbox/__init__.py' |
301 | --- tests/blackbox/__init__.py 2010-05-03 03:03:34 +0000 |
302 | +++ tests/blackbox/__init__.py 2011-05-08 15:53:28 +0000 |
303 | @@ -18,12 +18,11 @@ |
304 | # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
305 | # |
306 | |
307 | -from bzrlib.tests import TestUtil |
308 | - |
309 | |
310 | def load_tests(standard_tests, module, loader): |
311 | testmod_names = [ |
312 | 'test_builddeb', |
313 | + 'test_dep3', |
314 | 'test_do', |
315 | 'test_import_dsc', |
316 | 'test_import_upstream', |
317 | |
318 | === added file 'tests/blackbox/test_dep3.py' |
319 | --- tests/blackbox/test_dep3.py 1970-01-01 00:00:00 +0000 |
320 | +++ tests/blackbox/test_dep3.py 2011-05-08 15:53:28 +0000 |
321 | @@ -0,0 +1,104 @@ |
322 | +# test_dep3.py -- Blackbox tests for dep3-patch. |
323 | +# Copyright (C) 2011 Canonical Ltd. |
324 | +# |
325 | +# This file is part of bzr-builddeb. |
326 | +# |
327 | +# bzr-builddeb is free software; you can redistribute it and/or modify |
328 | +# it under the terms of the GNU General Public License as published by |
329 | +# the Free Software Foundation; either version 2 of the License, or |
330 | +# (at your option) any later version. |
331 | +# |
332 | +# bzr-builddeb is distributed in the hope that it will be useful, |
333 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
334 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
335 | +# GNU General Public License for more details. |
336 | +# |
337 | +# You should have received a copy of the GNU General Public License |
338 | +# along with bzr-builddeb; if not, write to the Free Software |
339 | +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
340 | +# |
341 | + |
342 | + |
343 | +"""Blackbox tests for "bzr dep3-patch".""" |
344 | + |
345 | + |
346 | +from bzrlib.tests.blackbox import ExternalBase |
347 | + |
348 | +import os |
349 | + |
350 | + |
351 | +class TestDep3Patch(ExternalBase): |
352 | + |
353 | + def setUp(self): |
354 | + super(TestDep3Patch, self).setUp() |
355 | + self.upstream_tree = self.make_branch_and_tree("upstream") |
356 | + self.upstream_tree.commit(message="initial commit") |
357 | + packaging = self.upstream_tree.bzrdir.sprout("packaging") |
358 | + self.packaging_tree = packaging.open_workingtree() |
359 | + feature = self.upstream_tree.bzrdir.sprout("feature") |
360 | + self.feature_tree = feature.open_workingtree() |
361 | + |
362 | + def test_nothing_to_do(self): |
363 | + (out, err) = self.run_bzr("dep3-patch -d packaging feature", retcode=3) |
364 | + self.assertEquals("bzr: ERROR: No unmerged revisions\n", err) |
365 | + self.assertEquals("", out) |
366 | + |
367 | + def test_simple(self): |
368 | + # If there is a single revision the commit message from |
369 | + # that revision will be used. |
370 | + self.build_tree_contents([("feature/foo", "bar\n")]) |
371 | + self.feature_tree.add("foo") |
372 | + self.feature_tree.commit(message="A message", timestamp=1304850124, |
373 | + timezone=0, authors=["Jelmer <jelmer@debian.org>"], rev_id="therevid") |
374 | + (out, err) = self.run_bzr("dep3-patch -d packaging feature") |
375 | + self.assertEquals(out, "Description: A message\n" |
376 | + "Origin: commit, revision id: therevid\n" |
377 | + "Author: Jelmer <jelmer@debian.org>\n" |
378 | + "Last-Update: 2011-05-08\n" |
379 | + "X-Bzr-Revision-Id: therevid\n" |
380 | + "\n" |
381 | + "=== added file 'foo'\n" |
382 | + "--- old/foo\t1970-01-01 00:00:00 +0000\n" |
383 | + "+++ new/foo\t2011-05-08 10:22:04 +0000\n" |
384 | + "@@ -0,0 +1,1 @@\n" |
385 | + "+bar\n" |
386 | + "\n") |
387 | + |
388 | + def test_uses_single_revision_commit(self): |
389 | + # If there is a single revision the commit message from |
390 | + # that revision will be used. |
391 | + self.feature_tree.commit(message="A message", timestamp=1304850124, |
392 | + timezone=0, authors=["Jelmer <jelmer@debian.org>"]) |
393 | + (out, err) = self.run_bzr("dep3-patch -d packaging feature") |
394 | + self.assertContainsRe(out, "Description: A message\n") |
395 | + |
396 | + def test_uses_config_description(self): |
397 | + config = self.feature_tree.branch.get_config() |
398 | + config.set_user_option("description", "What this does") |
399 | + self.feature_tree.commit(message="a message") |
400 | + (out, err) = self.run_bzr("dep3-patch -d packaging feature") |
401 | + self.assertContainsRe(out, "Description: What this does\n") |
402 | + |
403 | + def test_upstream_branch(self): |
404 | + os.mkdir('packaging/.bzr-builddeb/') |
405 | + f = open('packaging/.bzr-builddeb/local.conf', 'wb') |
406 | + try: |
407 | + f.write('[BUILDDEB]\nupstream-branch = %s\n' % |
408 | + self.upstream_tree.branch.base) |
409 | + finally: |
410 | + f.close() |
411 | + self.feature_tree.commit(message="a message") |
412 | + (out, err) = self.run_bzr("dep3-patch -d packaging feature") |
413 | + self.assertContainsRe(out, "Applied-Upstream: no\n") |
414 | + |
415 | + def test_upstream_branch_disabled(self): |
416 | + os.mkdir('packaging/.bzr-builddeb/') |
417 | + f = open('packaging/.bzr-builddeb/local.conf', 'wb') |
418 | + try: |
419 | + f.write('[BUILDDEB]\nupstream-branch = %s\n' % |
420 | + self.upstream_tree.branch.base) |
421 | + finally: |
422 | + f.close() |
423 | + self.feature_tree.commit(message="a message") |
424 | + (out, err) = self.run_bzr("dep3-patch --no-upstream-check -d packaging feature") |
425 | + self.assertNotContainsRe(out, "Applied-Upstream") |
426 | |
427 | === added file 'tests/test_dep3.py' |
428 | --- tests/test_dep3.py 1970-01-01 00:00:00 +0000 |
429 | +++ tests/test_dep3.py 2011-05-08 15:53:28 +0000 |
430 | @@ -0,0 +1,217 @@ |
431 | +# test_dep3.py -- Testsuite for builddeb dep3.py |
432 | +# Copyright (C) 2011 Canonical Ltd. |
433 | +# |
434 | +# This file is part of bzr-builddeb. |
435 | +# |
436 | +# bzr-builddeb is free software; you can redistribute it and/or modify |
437 | +# it under the terms of the GNU General Public License as published by |
438 | +# the Free Software Foundation; either version 2 of the License, or |
439 | +# (at your option) any later version. |
440 | +# |
441 | +# bzr-builddeb is distributed in the hope that it will be useful, |
442 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
443 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
444 | +# GNU General Public License for more details. |
445 | +# |
446 | +# You should have received a copy of the GNU General Public License |
447 | +# along with bzr-builddeb; if not, write to the Free Software |
448 | +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
449 | +# |
450 | + |
451 | +from cStringIO import StringIO |
452 | + |
453 | +import rfc822 |
454 | + |
455 | +from bzrlib.revision import ( |
456 | + NULL_REVISION, |
457 | + ) |
458 | +from bzrlib.tests import ( |
459 | + TestCase, |
460 | + TestCaseWithTransport, |
461 | + ) |
462 | + |
463 | +from bzrlib.plugins.builddeb.dep3 import ( |
464 | + describe_origin, |
465 | + determine_applied_upstream, |
466 | + gather_bugs_and_authors, |
467 | + write_dep3_bug_line, |
468 | + write_dep3_patch, |
469 | + write_dep3_patch_header, |
470 | + ) |
471 | + |
472 | + |
473 | +class Dep3HeaderTests(TestCase): |
474 | + |
475 | + def dep3_header(self, description=None, origin=None, forwarded=None, |
476 | + bugs=None, authors=None, revision_id=None, last_update=None, |
477 | + applied_upstream=None): |
478 | + f = StringIO() |
479 | + write_dep3_patch_header(f, description=description, origin=origin, |
480 | + forwarded=forwarded, bugs=bugs, authors=authors, |
481 | + revision_id=revision_id, last_update=last_update, |
482 | + applied_upstream=applied_upstream) |
483 | + f.seek(0) |
484 | + return rfc822.Message(f) |
485 | + |
486 | + def test_description(self): |
487 | + ret = self.dep3_header(description="This patch fixes the foobar") |
488 | + self.assertEquals("This patch fixes the foobar", ret["Description"]) |
489 | + |
490 | + def test_last_updated(self): |
491 | + ret = self.dep3_header(last_update=1304840034) |
492 | + self.assertEquals("2011-05-08", ret["Last-Update"]) |
493 | + |
494 | + def test_revision_id(self): |
495 | + ret = self.dep3_header(revision_id="myrevid") |
496 | + self.assertEquals("myrevid", ret["X-Bzr-Revision-Id"]) |
497 | + |
498 | + def test_authors(self): |
499 | + authors = [ |
500 | + "Jelmer Vernooij <jelmer@canonical.com>", |
501 | + "James Westby <james.westby@canonical.com>"] |
502 | + ret = self.dep3_header(authors=authors) |
503 | + self.assertEquals([ |
504 | + ("Jelmer Vernooij", "jelmer@canonical.com"), |
505 | + ("James Westby", "james.westby@canonical.com")], |
506 | + ret.getaddrlist("Author")) |
507 | + |
508 | + def test_origin(self): |
509 | + ret = self.dep3_header(origin="Cherrypick from upstream") |
510 | + self.assertEquals("Cherrypick from upstream", |
511 | + ret["Origin"]) |
512 | + |
513 | + def test_forwarded(self): |
514 | + ret = self.dep3_header(forwarded="not needed") |
515 | + self.assertEquals("not needed", |
516 | + ret["Forwarded"]) |
517 | + |
518 | + def test_applied_upstream(self): |
519 | + ret = self.dep3_header(applied_upstream="commit 45") |
520 | + self.assertEquals("commit 45", ret["Applied-Upstream"]) |
521 | + |
522 | + def test_bugs(self): |
523 | + bugs = [ |
524 | + ("http://bugs.debian.org/424242", "fixed"), |
525 | + ("https://bugs.launchpad.net/bugs/20110508", "fixed"), |
526 | + ("http://bugzilla.samba.org/bug.cgi?id=52", "fixed")] |
527 | + ret = self.dep3_header(bugs=bugs) |
528 | + self.assertEquals([ |
529 | + "https://bugs.launchpad.net/bugs/20110508", |
530 | + "http://bugzilla.samba.org/bug.cgi?id=52"], |
531 | + ret.getheaders("Bug")) |
532 | + self.assertEquals(["http://bugs.debian.org/424242"], |
533 | + ret.getheaders("Bug-Debian")) |
534 | + |
535 | + def test_write_bug_fix_only(self): |
536 | + # non-fixed bug lines are ignored |
537 | + f = StringIO() |
538 | + write_dep3_bug_line(f, "http://bar/", "pending") |
539 | + self.assertEquals("", f.getvalue()) |
540 | + |
541 | + def test_write_normal_bug(self): |
542 | + f = StringIO() |
543 | + write_dep3_bug_line(f, "http://bugzilla.samba.org/bug.cgi?id=42", |
544 | + "fixed") |
545 | + self.assertEquals("Bug: http://bugzilla.samba.org/bug.cgi?id=42\n", |
546 | + f.getvalue()) |
547 | + |
548 | + def test_write_debian_bug(self): |
549 | + f = StringIO() |
550 | + write_dep3_bug_line(f, "http://bugs.debian.org/234354", "fixed") |
551 | + self.assertEquals("Bug-Debian: http://bugs.debian.org/234354\n", |
552 | + f.getvalue()) |
553 | + |
554 | + |
555 | +class GatherBugsAndAuthors(TestCaseWithTransport): |
556 | + |
557 | + def test_none(self): |
558 | + branch = self.make_branch(".") |
559 | + self.assertEquals((set(), set(), None), |
560 | + gather_bugs_and_authors(branch.repository, [])) |
561 | + |
562 | + def test_multiple_authors(self): |
563 | + tree = self.make_branch_and_tree(".") |
564 | + revid1 = tree.commit(authors=["Jelmer Vernooij <jelmer@canonical.com>"], |
565 | + timestamp=1304844311, message="msg") |
566 | + revid2 = tree.commit(authors=["Max Bowsher <maxb@f2s.com>"], |
567 | + timestamp=1304844278, message="msg") |
568 | + self.assertEquals((set(), set([ |
569 | + "Jelmer Vernooij <jelmer@canonical.com>", |
570 | + "Max Bowsher <maxb@f2s.com>"]), 1304844311), |
571 | + gather_bugs_and_authors(tree.branch.repository, [revid1, revid2])) |
572 | + |
573 | + def test_bugs(self): |
574 | + tree = self.make_branch_and_tree(".") |
575 | + revid1 = tree.commit(authors=["Jelmer Vernooij <jelmer@canonical.com>"], |
576 | + timestamp=1304844311, message="msg", revprops={"bugs": |
577 | + "http://bugs.samba.org/bug.cgi?id=2011 fixed\n"}) |
578 | + self.assertEquals(( |
579 | + set([("http://bugs.samba.org/bug.cgi?id=2011", "fixed")]), |
580 | + set(["Jelmer Vernooij <jelmer@canonical.com>"]), 1304844311), |
581 | + gather_bugs_and_authors(tree.branch.repository, [revid1])) |
582 | + |
583 | + |
584 | +class DetermineAppliedUpstreamTests(TestCaseWithTransport): |
585 | + |
586 | + def test_not_applied(self): |
587 | + upstream = self.make_branch_and_tree("upstream") |
588 | + feature = self.make_branch_and_tree("feature") |
589 | + feature.commit(message="every bloody emperor") |
590 | + self.addCleanup(feature.lock_read().unlock) |
591 | + self.assertEquals("no", |
592 | + determine_applied_upstream(upstream.branch, feature.branch)) |
593 | + |
594 | + def test_merged(self): |
595 | + upstream = self.make_branch_and_tree("upstream") |
596 | + upstream.commit(message="initial upstream commit") |
597 | + feature = upstream.bzrdir.sprout("feature").open_workingtree() |
598 | + feature.commit(message="nutter alert") |
599 | + upstream.merge_from_branch(feature.branch) |
600 | + upstream.commit(message="merge feature") |
601 | + self.addCleanup(upstream.lock_read().unlock) |
602 | + self.addCleanup(feature.lock_read().unlock) |
603 | + self.assertEquals("merged in revision 2", |
604 | + determine_applied_upstream(upstream.branch, feature.branch)) |
605 | + |
606 | + |
607 | +class DescribeOriginTests(TestCaseWithTransport): |
608 | + |
609 | + def test_no_public_branch(self): |
610 | + tree = self.make_branch_and_tree(".") |
611 | + revid1 = tree.commit(message="msg1") |
612 | + self.assertEquals("commit, revision id: %s" % revid1, |
613 | + describe_origin(tree.branch, revid1)) |
614 | + |
615 | + def test_public_branch(self): |
616 | + tree = self.make_branch_and_tree(".") |
617 | + tree.branch.set_public_branch("http://example.com/public") |
618 | + revid1 = tree.commit(message="msg1") |
619 | + self.assertEquals("commit, http://example.com/public, revision: 1", |
620 | + describe_origin(tree.branch, revid1)) |
621 | + |
622 | + |
623 | +class FullDep3PatchTests(TestCaseWithTransport): |
624 | + |
625 | + def test_simple(self): |
626 | + f = StringIO() |
627 | + tree = self.make_branch_and_tree(".") |
628 | + self.build_tree_contents([("foo", "data")]) |
629 | + tree.add("foo") |
630 | + revid = tree.commit("msg", rev_id="arevid", timestamp=1304849661, |
631 | + timezone=0) |
632 | + write_dep3_patch(f, tree.branch, NULL_REVISION, revid, |
633 | + description="Nutter alert", |
634 | + forwarded="not needed", |
635 | + authors=set(["Jelmer <jelmer@samba.org>"])) |
636 | + self.assertEquals("Description: Nutter alert\n" |
637 | + "Forwarded: not needed\n" |
638 | + "Author: Jelmer <jelmer@samba.org>\n" |
639 | + "X-Bzr-Revision-Id: arevid\n" |
640 | + "\n" |
641 | + "=== added file 'foo'\n" |
642 | + "--- old/foo\t1970-01-01 00:00:00 +0000\n" |
643 | + "+++ new/foo\t2011-05-08 10:14:21 +0000\n" |
644 | + "@@ -0,0 +1,1 @@\n" |
645 | + "+data\n" |
646 | + "\\ No newline at end of file\n" |
647 | + "\n", f.getvalue()) |