Merge lp:~flacoste/launchpad/bug-781993-2 into lp:launchpad

Proposed by Francis J. Lacoste
Status: Superseded
Proposed branch: lp:~flacoste/launchpad/bug-781993-2
Merge into: lp:launchpad
Diff against target: 2233 lines (+681/-704) (has conflicts)
20 files modified
cronscripts/update-debwatches.py (+33/-22)
lib/canonical/launchpad/interfaces/validation.py (+7/-4)
lib/canonical/launchpad/scripts/debsync.py (+7/-9)
lib/lp/app/widgets/launchpadtarget.py (+3/-2)
lib/lp/bugs/browser/bugtarget.py (+14/-8)
lib/lp/bugs/browser/tests/bug-views.txt (+6/-18)
lib/lp/bugs/browser/tests/test_bugtarget_filebug.py (+32/-0)
lib/lp/bugs/browser/widgets/bugtask.py (+1/-1)
lib/lp/bugs/doc/bugtask-package-widget.txt (+2/-2)
lib/lp/bugs/doc/bugzilla-import.txt (+346/-337)
lib/lp/bugs/interfaces/bug.py (+1/-5)
lib/lp/bugs/model/bug.py (+1/-8)
lib/lp/bugs/scripts/bugzilla.py (+12/-9)
lib/lp/bugs/stories/guided-filebug/xx-bug-reporting-tools.txt (+12/-12)
lib/lp/bugs/xmlrpc/bug.py (+2/-1)
lib/lp/registry/interfaces/distribution.py (+15/-8)
lib/lp/registry/model/distribution.py (+36/-71)
lib/lp/registry/tests/test_distribution.py (+124/-3)
lib/lp/soyuz/doc/distribution.txt (+6/-176)
lib/lp/testing/factory.py (+21/-8)
Text conflict in lib/lp/bugs/browser/tests/bug-views.txt
To merge this branch: bzr merge lp:~flacoste/launchpad/bug-781993-2
Reviewer Review Type Date Requested Status
Launchpad code reviewers Pending
Review via email: mp+65057@code.launchpad.net

Description of the change

= Summary =

We want to be able to file bugs in the Ensemble Principia distribution
(https://launchpad.net/principia). That distribution doesn't have published
source package, but they have official source package branch.

It builds on the previous guessPublishedSourcePackageName refactoring.

== Proposed fix ==

Allow filing a bug on a source package if it has official source package
branch.

== Pre-implementation notes ==

== Implementation details ==

 * If no published source package is found, it looks for official source
 package branches for it.

== Tests ==

In addition to the unit test for guessPublishedSourcePackageName, I added an
integration test just to make sure that we don't regress that functionality.
(By changing the vocabulary for example, they are currently very lax and will
allow any valid source or binary package names. If we were to make them more
context sensitive, it might be a problem if official package branches weren't
considered.)

 ./bin/test -vvt 'test_guessPublished|TestFileBugSourcePackage'

== Demo and Q/A ==

Try filing a sourcepackage bug on qastaging on the the principia distribution.

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/bugs/stories/guided-filebug/xx-bug-reporting-tools.txt
  lib/lp/bugs/browser/bugtarget.py
  lib/lp/bugs/doc/bugzilla-import.txt
  lib/lp/bugs/browser/widgets/bugtask.py
  lib/lp/bugs/browser/tests/bug-views.txt
  lib/canonical/launchpad/interfaces/validation.py
  lib/lp/testing/factory.py
  lib/lp/bugs/browser/tests/test_bugtarget_filebug.py
  lib/lp/bugs/model/bug.py
  lib/lp/soyuz/doc/distribution.txt
  cronscripts/update-debwatches.py
  lib/lp/bugs/doc/bugtask-package-widget.txt
  lib/lp/registry/interfaces/distribution.py
  lib/lp/app/widgets/launchpadtarget.py
  lib/canonical/launchpad/scripts/debsync.py
  lib/lp/registry/tests/test_distribution.py
  lib/lp/bugs/scripts/bugzilla.py
  lib/lp/registry/model/distribution.py
  lib/lp/bugs/xmlrpc/bug.py
  lib/lp/bugs/interfaces/bug.py

./lib/lp/bugs/browser/tests/bug-views.txt
     195: source has trailing whitespace.
./lib/lp/bugs/model/bug.py
      52: 'SQLRaw' imported but unused
      52: 'Join' imported but unused
      52: 'Exists' imported but unused
     171: 'get_bug_privacy_filter' imported but unused
      52: 'Count' imported but unused
     304: E301 expected 1 blank line, found 0
    2590: E225 missing whitespace around operator
./cronscripts/update-debwatches.py
       9: '_pythonpath' imported but unused
./lib/lp/bugs/scripts/bugzilla.py
       8: Line exceeds 78 characters.

I'll fix this post-review.

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'cronscripts/update-debwatches.py'
2--- cronscripts/update-debwatches.py 2011-05-29 01:36:05 +0000
3+++ cronscripts/update-debwatches.py 2011-06-17 19:56:04 +0000
4@@ -16,6 +16,7 @@
5 from zope.component import getUtility
6
7 from canonical.database.constants import UTC_NOW
8+from lp.app.errors import NotFoundError
9 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
10 from lp.bugs.interfaces.bug import IBugSet
11 from lp.bugs.interfaces.bugtask import (
12@@ -41,7 +42,8 @@
13 loglevel = logging.WARNING
14
15 def add_my_options(self):
16- self.parser.add_option('--max', action='store', type='int', dest='max',
17+ self.parser.add_option(
18+ '--max', action='store', type='int', dest='max',
19 default=None, help="The maximum number of bugs to synchronise.")
20 self.parser.add_option('--debbugs', action='store', type='string',
21 dest='debbugs',
22@@ -49,7 +51,8 @@
23 help="The location of your debbugs database.")
24
25 def main(self):
26- if not os.path.exists(os.path.join(self.options.debbugs, 'index/index.db')):
27+ if not os.path.exists(
28+ os.path.join(self.options.debbugs, 'index/index.db')):
29 raise LaunchpadScriptFailure('%s is not a debbugs db.'
30 % self.options.debbugs)
31
32@@ -68,10 +71,12 @@
33 debwatches = debbugs_tracker.watches
34
35 previousimportset = set([b.remotebug for b in debwatches])
36- self.logger.info('%d debbugs previously imported.' % len(previousimportset))
37+ self.logger.info(
38+ '%d debbugs previously imported.' % len(previousimportset))
39
40 target_watches = [watch for watch in debwatches if watch.needscheck]
41- self.logger.info('%d debbugs watches to syncronise.' % len(target_watches))
42+ self.logger.info(
43+ '%d debbugs watches to syncronise.' % len(target_watches))
44
45 self.logger.info('Sorting bug watches...')
46 target_watches.sort(key=lambda a: a.remotebug)
47@@ -100,12 +105,14 @@
48 debian = getUtility(ILaunchpadCelebrities).debian
49 debbugs_tracker = getUtility(ILaunchpadCelebrities).debbugs
50
51- # make sure we have tasks for all the debian package linkages, and also
52- # make sure we have updated their status and severity appropriately
53+ # make sure we have tasks for all the debian package linkages, and
54+ # also make sure we have updated their status and severity
55+ # appropriately.
56 for packagename in debian_bug.packagelist():
57 try:
58- srcpkgname, binpkgname = debian.guessPackageNames(packagename)
59- except ValueError:
60+ srcpkgname = debian.guessPublishedSourcePackageName(
61+ packagename)
62+ except NotFoundError:
63 self.logger.error(sys.exc_value)
64 continue
65 search_params = BugTaskSearchParams(user=None, bug=malone_bug,
66@@ -114,8 +121,8 @@
67 bugtasks = bugtaskset.search(search_params)
68 if len(bugtasks) == 0:
69 # we need a new task to link the bug to the debian package
70- self.logger.info('Linking %d and debian %s/%s' % (
71- malone_bug.id, srcpkgname.name, binpkgname.name))
72+ self.logger.info('Linking %d and debian %s' % (
73+ malone_bug.id, srcpkgname.name))
74 # XXX: kiko 2007-02-03:
75 # This code is completely untested and broken.
76 bugtask = malone_bug.addTask(
77@@ -127,10 +134,11 @@
78 assert len(bugtasks) == 1, 'Should only find a single task'
79 bugtask = bugtasks[0]
80 status = bugtask.status
81- if status <> bugtask.setStatusFromDebbugs(debian_bug.status):
82+ if status != bugtask.setStatusFromDebbugs(debian_bug.status):
83 waschanged = True
84 severity = bugtask.severity
85- if severity <> bugtask.setSeverityFromDebbugs(debian_bug.severity):
86+ if severity != bugtask.setSeverityFromDebbugs(
87+ debian_bug.severity):
88 waschanged = True
89
90 known_msg_ids = set([msg.rfc822msgid for msg in malone_bug.messages])
91@@ -157,17 +165,18 @@
92 if msg is None:
93 continue
94
95- # create the link between the bug and this message
96- bugmsg = malone_bug.linkMessage(msg)
97+ # Create the link between the bug and this message.
98+ malone_bug.linkMessage(msg)
99
100- # ok, this is a new message for this bug, so in effect something has
101- # changed
102+ # ok, this is a new message for this bug, so in effect something
103+ # has changed
104 waschanged = True
105
106 # now we need to analyse the message for useful data
107 watches = bugwatchset.fromMessage(msg, malone_bug)
108 for watch in watches:
109- self.logger.info('New watch for #%s on %s' % (watch.bug.id, watch.url))
110+ self.logger.info(
111+ 'New watch for #%s on %s' % (watch.bug.id, watch.url))
112 waschanged = True
113
114 # and also for CVE ref clues
115@@ -191,7 +200,8 @@
116 if (len(debian_bug.mergedwith) > 0 and
117 min(debian_bug.mergedwith) > debian_bug.id):
118 for merged_id in debian_bug.mergedwith:
119- merged_bug = bugset.queryByRemoteBug(debbugs_tracker, merged_id)
120+ merged_bug = bugset.queryByRemoteBug(
121+ debbugs_tracker, merged_id)
122 if merged_bug is not None:
123 # Bug has been imported already
124 if merged_bug.duplicateof == malone_bug:
125@@ -199,11 +209,13 @@
126 continue
127 elif merged_bug.duplicateof is not None:
128 # Interesting, we think it's a dup of something else
129- self.logger.warning('Debbugs thinks #%d is a dup of #%d' % (
130+ self.logger.warning(
131+ 'Debbugs thinks #%d is a dup of #%d' % (
132 merged_bug.id, merged_bug.duplicateof))
133 continue
134 # Go ahead and merge it
135- self.logger.info("Malone #%d is a duplicate of Malone #%d" % (
136+ self.logger.info(
137+ "Malone #%d is a duplicate of Malone #%d" % (
138 merged_bug.id, malone_bug.id))
139 merged_bug.duplicateof = malone_bug.id
140
141@@ -211,7 +223,7 @@
142 waschanged = True
143
144 # make a note of the remote watch status, if it has changed
145- if watch.remotestatus <> debian_bug.status:
146+ if watch.remotestatus != debian_bug.status:
147 watch.remotestatus = debian_bug.status
148 waschanged = True
149
150@@ -226,4 +238,3 @@
151 if __name__ == '__main__':
152 script = DebWatchUpdater('launchpad-debbugs-sync')
153 script.lock_and_run()
154-
155
156=== modified file 'lib/canonical/launchpad/interfaces/validation.py'
157--- lib/canonical/launchpad/interfaces/validation.py 2011-04-11 02:32:50 +0000
158+++ lib/canonical/launchpad/interfaces/validation.py 2011-06-17 19:56:04 +0000
159@@ -95,6 +95,7 @@
160 "${email} isn't a valid email address.",
161 mapping={'email': email}))
162
163+
164 def _check_email_availability(email):
165 email_address = getUtility(IEmailAddressSet).getByEmail(email)
166 if email_address is not None:
167@@ -198,7 +199,8 @@
168 # If the distribution has at least one series, check that the
169 # source package has been published in the distribution.
170 try:
171- distribution.guessPackageNames(sourcepackagename.name)
172+ distribution.guessPublishedSourcePackageName(
173+ sourcepackagename.name)
174 except NotFoundError, e:
175 raise LaunchpadValidationError(e)
176 new_source_package = distribution.getSourcePackage(sourcepackagename)
177@@ -232,9 +234,10 @@
178 user = getUtility(ILaunchBag).user
179 params = BugTaskSearchParams(user, bug=bug)
180 if not bug_target.searchTasks(params).is_empty():
181- errors.append(LaunchpadValidationError(_(
182- 'A fix for this bug has already been requested for ${target}',
183- mapping={'target': bug_target.displayname})))
184+ errors.append(
185+ LaunchpadValidationError(_(
186+ 'A fix for this bug has already been requested for ${target}',
187+ mapping={'target': bug_target.displayname})))
188
189 if len(errors) > 0:
190 raise expose(WidgetsError(errors), 400)
191
192=== modified file 'lib/canonical/launchpad/scripts/debsync.py'
193--- lib/canonical/launchpad/scripts/debsync.py 2011-05-27 21:12:25 +0000
194+++ lib/canonical/launchpad/scripts/debsync.py 2011-06-17 19:56:04 +0000
195@@ -17,6 +17,7 @@
196 from zope.component import getUtility
197
198 from canonical.database.sqlbase import flush_database_updates
199+from lp.app.errors import NotFoundError
200 from lp.app.interfaces.launchpad import ILaunchpadCelebrities
201 from lp.bugs.interfaces.bug import (
202 CreateBugParams,
203@@ -60,7 +61,7 @@
204 return False
205 # and we won't import any bug that is newer than one week, to give
206 # debian some time to find dups
207- if bug.date > datetime.datetime.now()-datetime.timedelta(minimum_age):
208+ if bug.date > datetime.datetime.now() - datetime.timedelta(minimum_age):
209 return False
210 return True
211
212@@ -103,7 +104,6 @@
213
214 def import_bug(debian_bug, logger):
215 """Consider importing a debian bug, return True if you did."""
216- packagelist = debian_bug.packagelist()
217 bugset = getUtility(IBugSet)
218 debbugs_tracker = getUtility(ILaunchpadCelebrities).debbugs
219 malone_bug = bugset.queryByRemoteBug(debbugs_tracker, debian_bug.id)
220@@ -143,11 +143,11 @@
221 # debian_bug.packagelist[0] is going to be a single package name for
222 # sure. we work through the package list, try to find one we can
223 # work with, otherwise give up
224- srcpkg = binpkg = pkgname = None
225+ srcpkg = pkgname = None
226 for pkgname in debian_bug.packagelist():
227 try:
228- srcpkg, binpkg = ubuntu.guessPackageNames(pkgname)
229- except ValueError:
230+ srcpkg = ubuntu.guessPublishedSourcePackageName(pkgname)
231+ except NotFoundError:
232 logger.error(sys.exc_value)
233 if srcpkg is None:
234 # none of the package names gave us a source package we can use
235@@ -158,8 +158,8 @@
236 return False
237 # sometimes debbugs has initial emails that contain the package name, we
238 # can remove that
239- if title.startswith(pkgname+':'):
240- title = title[len(pkgname)+2:].strip()
241+ if title.startswith(pkgname + ':'):
242+ title = title[len(pkgname) + 2:].strip()
243 params = CreateBugParams(
244 title=title, msg=msg, owner=msg.owner,
245 datecreated=msg.datecreated)
246@@ -199,5 +199,3 @@
247
248 flush_database_updates()
249 return True
250-
251-
252
253=== modified file 'lib/lp/app/widgets/launchpadtarget.py'
254--- lib/lp/app/widgets/launchpadtarget.py 2011-05-27 21:12:25 +0000
255+++ lib/lp/app/widgets/launchpadtarget.py 2011-06-17 19:56:04 +0000
256@@ -135,8 +135,9 @@
257 if package_name is None:
258 return distribution
259 try:
260- source_name, binary_name = distribution.guessPackageNames(
261- package_name.name)
262+ source_name = (
263+ distribution.guessPublishedSourcePackageName(
264+ package_name.name))
265 except NotFoundError:
266 raise LaunchpadValidationError(
267 "There is no package name '%s' published in %s"
268
269=== modified file 'lib/lp/bugs/browser/bugtarget.py'
270--- lib/lp/bugs/browser/bugtarget.py 2011-06-07 23:20:43 +0000
271+++ lib/lp/bugs/browser/bugtarget.py 2011-06-17 19:56:04 +0000
272@@ -329,6 +329,17 @@
273 self.extra_data = FileBugData()
274
275 def initialize(self):
276+ # redirect_ubuntu_filebug is a cached_property.
277+ # Access it first just to compute its value. Because it
278+ # makes a DB access to get the bug supervisor, it causes
279+ # trouble in tests when form validation errors occur. Because the
280+ # transaction is doomed, the storm cache is invalidated and accessing
281+ # the property will result in a a LostObjectError, because
282+ # the created objects disappeared. Not likely a problem in production
283+ # since the objects will still be in the DB, but doesn't hurt there
284+ # either. It makes for better diagnosis of failing tests.
285+ if self.redirect_ubuntu_filebug:
286+ pass
287 LaunchpadFormView.initialize(self)
288 if (not self.redirect_ubuntu_filebug and
289 self.extra_data_token is not None and
290@@ -473,7 +484,7 @@
291 distribution = self.context.distribution
292
293 try:
294- distribution.guessPackageNames(packagename)
295+ distribution.guessPublishedSourcePackageName(packagename)
296 except NotFoundError:
297 if distribution.series:
298 # If a distribution doesn't have any series,
299@@ -590,13 +601,9 @@
300 # package name, so let the Soyuz API figure it out for us.
301 packagename = str(packagename.name)
302 try:
303- sourcepackagename, binarypackagename = (
304- context.guessPackageNames(packagename))
305+ sourcepackagename = context.guessPublishedSourcePackageName(
306+ packagename)
307 except NotFoundError:
308- # guessPackageNames may raise NotFoundError. It would be
309- # nicer to allow people to indicate a package even if
310- # never published, but the quick fix for now is to note
311- # the issue and move on.
312 notifications.append(
313 "The package %s is not published in %s; the "
314 "bug was targeted only to the distribution."
315@@ -608,7 +615,6 @@
316 packagename, context.displayname))
317 else:
318 context = context.getSourcePackage(sourcepackagename.name)
319- params.binarypackagename = binarypackagename
320
321 extra_data = self.extra_data
322 if extra_data.extra_description:
323
324=== modified file 'lib/lp/bugs/browser/tests/bug-views.txt'
325--- lib/lp/bugs/browser/tests/bug-views.txt 2011-06-16 13:50:58 +0000
326+++ lib/lp/bugs/browser/tests/bug-views.txt 2011-06-17 19:56:04 +0000
327@@ -25,8 +25,7 @@
328 if the user provides a valid package name when reporting the bug.
329
330 If the package name entered by the user happens to be a binary package
331-name, that information is recorded in the description, and the first
332-comment, of the bug report.
333+name, the bug is filed against the source package used to build that binary.
334
335 >>> from zope.component import getMultiAdapter, getUtility
336 >>> from canonical.launchpad.webapp.interfaces import (
337@@ -70,16 +69,7 @@
338
339 >>> latest_ubuntu_bugtask = ubuntu.searchTasks(search_params)[0]
340
341-The user specified a binary package name, so that's been added to the
342-bug description and the first comment:
343-
344- >>> print latest_ubuntu_bugtask.bug.description
345- Binary package hint: linux-2.6.12
346- <BLANKLINE>
347- <BLANKLINE>
348- a bug in a bin pkg
349-
350-the source package from which the binary was built has been set on
351+The source package from which the binary was built has been set on
352 the bugtask.
353
354 >>> print latest_ubuntu_bugtask.sourcepackagename.name
355@@ -166,7 +156,11 @@
356 3
357 >>> [ (c.index, c.owner.name, c.text_contents)
358 ... for c in ubuntu_bugview.comments ]
359+<<<<<<< TREE
360 [(0, u'name16', u'Binary package hint: linux-2.6.12...'),
361+=======
362+ [(0, u'name16', u'a bug in a bin pkg'),
363+>>>>>>> MERGE-SOURCE
364 (1, u'name16', u'I can reproduce this bug.'),
365 (2, u'name16', u'I can reproduce this bug.')]
366
367@@ -178,17 +172,11 @@
368 identical. Take as an example the first bug posted above:
369
370 >>> print latest_ubuntu_bugtask.bug.description
371- Binary package hint: linux-2.6.12
372- <BLANKLINE>
373- <BLANKLINE>
374 a bug in a bin pkg
375
376 Its description has the same contents as the bug's first comment:
377
378 >>> print latest_ubuntu_bugtask.bug.messages[0].text_contents
379- Binary package hint: linux-2.6.12
380- <BLANKLINE>
381- <BLANKLINE>
382 a bug in a bin pkg
383
384 The view class offers a method to check exactly that:
385
386=== modified file 'lib/lp/bugs/browser/tests/test_bugtarget_filebug.py'
387--- lib/lp/bugs/browser/tests/test_bugtarget_filebug.py 2011-04-29 01:55:28 +0000
388+++ lib/lp/bugs/browser/tests/test_bugtarget_filebug.py 2011-06-17 19:56:04 +0000
389@@ -8,6 +8,7 @@
390 TooLong,
391 TooShort,
392 )
393+from zope.security.proxy import removeSecurityProxy
394
395 from canonical.launchpad.ftests import login
396 from canonical.launchpad.testing.pages import (
397@@ -21,6 +22,7 @@
398 FileBugViewBase,
399 )
400 from lp.bugs.interfaces.bug import IBugAddForm
401+from lp.bugs.publisher import BugsLayer
402 from lp.testing import (
403 login_person,
404 TestCaseWithFactory,
405@@ -394,3 +396,33 @@
406 "source": product.displayname, "content": u"Include bug details",
407 }]
408 self.assertEqual(expected_guidelines, view.bug_reporting_guidelines)
409+
410+
411+class TestFileBugSourcePackage(TestCaseWithFactory):
412+
413+ layer = DatabaseFunctionalLayer
414+
415+ def test_filebug_works_on_official_package_branch(self):
416+ # It should be possible to file a bug against a source package
417+ # when there is an official package branch.
418+ user = self.factory.makePerson()
419+ sourcepackage = self.factory.makeSourcePackage('my-package')
420+ self.factory.makeRelatedBranchesForSourcePackage(
421+ sourcepackage=sourcepackage)
422+ removeSecurityProxy(sourcepackage.distribution).official_malone = True
423+ login_person(user)
424+
425+ view = create_initialized_view(
426+ context=sourcepackage.distribution, name='+filebug',
427+ form={
428+ 'field.title': 'A bug',
429+ 'field.comment': 'A comment',
430+ 'field.bugtarget.distribution': (
431+ sourcepackage.distribution.name),
432+ 'field.packagename': 'my-package',
433+ 'field.actions.submit_bug': 'Submit Bug Request',
434+ }, layer=BugsLayer, principal=user)
435+ msg = "\n".join([
436+ notification.message
437+ for notification in view.request.response.notifications])
438+ self.assertIn("Thank you for your bug report.", msg)
439
440=== modified file 'lib/lp/bugs/browser/widgets/bugtask.py'
441--- lib/lp/bugs/browser/widgets/bugtask.py 2011-06-02 21:31:51 +0000
442+++ lib/lp/bugs/browser/widgets/bugtask.py 2011-06-17 19:56:04 +0000
443@@ -496,7 +496,7 @@
444 distribution = self.getDistribution()
445
446 try:
447- source, binary = distribution.guessPackageNames(input)
448+ source = distribution.guessPublishedSourcePackageName(input)
449 except NotFoundError:
450 try:
451 return self.convertTokensToValues([input])[0]
452
453=== modified file 'lib/lp/bugs/doc/bugtask-package-widget.txt'
454--- lib/lp/bugs/doc/bugtask-package-widget.txt 2011-01-11 11:51:27 +0000
455+++ lib/lp/bugs/doc/bugtask-package-widget.txt 2011-06-17 19:56:04 +0000
456@@ -49,13 +49,13 @@
457 u'linux-source-2.6.15'
458
459 For some distribution we don't know exactly which source packages it
460-contains, so IDistribution.guessPackageNames will return a
461+contains, so IDistribution.guessPublishedSourcePackageName will raise a
462 NotFoundError.
463
464 >>> debian_task = bug_one.bugtasks[-1]
465 >>> debian_task.distribution.name
466 u'debian'
467- >>> debian_task.distribution.guessPackageNames('evolution')
468+ >>> debian_task.distribution.guessPublishedSourcePackageName('evolution')
469 Traceback (most recent call last):
470 ...
471 NotFoundError...
472
473=== modified file 'lib/lp/bugs/doc/bugzilla-import.txt'
474--- lib/lp/bugs/doc/bugzilla-import.txt 2011-05-27 19:53:20 +0000
475+++ lib/lp/bugs/doc/bugzilla-import.txt 2011-06-17 19:56:04 +0000
476@@ -8,220 +8,222 @@
477 We will start by defining a fake backend and some fake information for
478 it to return:
479
480- >>> from datetime import datetime
481- >>> import pytz
482- >>> UTC = pytz.timezone('UTC')
483-
484- >>> users = [
485- ... ('test@canonical.com', 'Sample User'),
486- ... ('foo.bar@canonical.com', 'Foo Bar'),
487- ... ('new.user@canonical.com', 'New User') # <- not in Launchpad
488- ... ]
489-
490- >>> buginfo = [
491- ... (1, # bug_id
492- ... 1, # assigned_to
493- ... '', # bug_file_loc
494- ... 'normal', # bug_severity
495- ... 'NEW', # status
496- ... datetime(2005, 4, 1, tzinfo=UTC), # creation
497- ... 'Test bug 1', # short_desc,
498- ... 'Linux', # op_sys
499- ... 'P2', # priority
500- ... 'Ubuntu', # product
501- ... 'AMD64', # rep_platform
502- ... 1, # reporter
503- ... '---', # version
504- ... 'mozilla-firefox', # component
505- ... '', # resolution
506- ... 'Ubuntu 5.10', # milestone
507- ... 0, # qa_contact
508- ... 'status', # status_whiteboard
509- ... '', # keywords
510- ... ''), # alias
511- ... # A WONTFIX bug on a non-existant distro package
512- ... (2, 1, 'http://www.ubuntu.com', 'enhancement', 'RESOLVED',
513- ... datetime(2005, 4, 2, tzinfo=UTC), 'Test bug 2',
514- ... 'Linux', 'P1', 'Ubuntu', 'i386', 2, '---', 'unknown',
515- ... 'WONTFIX', '---', 0, '', '', ''),
516- ... # An accepted bug:
517- ... (3, 2, 'http://www.ubuntu.com', 'blocker', 'ASSIGNED',
518- ... datetime(2005, 4, 3, tzinfo=UTC), 'Test bug 3',
519- ... 'Linux', 'P1', 'Ubuntu', 'i386', 1, '---', 'netapplet',
520- ... '', '---', 0, '', '', 'xyz'),
521- ... # A fixed bug
522- ... (4, 1, 'http://www.ubuntu.com', 'blocker', 'CLOSED',
523- ... datetime(2005, 4, 4, tzinfo=UTC), 'Test bug 4',
524- ... 'Linux', 'P1', 'Ubuntu', 'i386', 1, '---', 'mozilla-firefox',
525- ... 'FIXED', '---', 0, '', '', 'FooBar'),
526- ... # An UPSTREAM bug
527- ... (5, 1, 'http://bugzilla.gnome.org/bugs/show_bug.cgi?id=273041',
528- ... 'blocker', 'UPSTREAM',
529- ... datetime(2005, 4, 4, tzinfo=UTC), 'Test bug 5',
530- ... 'Linux', 'P1', 'Ubuntu', 'i386', 1, '---', 'evolution',
531- ... '', '---', 0, '', '', 'deb1234'),
532- ... ]
533-
534- >>> ccs = [[], [3], [], [], []]
535-
536- >>> comments = [
537- ... [(1, datetime(2005, 4, 1, tzinfo=UTC), 'First comment'),
538- ... (2, datetime(2005, 4, 1, 1, tzinfo=UTC), 'Second comment')],
539- ... [(1, datetime(2005, 4, 2, tzinfo=UTC), 'First comment'),
540- ... (2, datetime(2005, 4, 2, 1, tzinfo=UTC), 'Second comment')],
541- ... [(2, datetime(2005, 4, 3, tzinfo=UTC), 'First comment'),
542- ... (1, datetime(2005, 4, 3, 1, tzinfo=UTC),
543- ... 'This is related to CVE-2005-1234'),
544- ... (2, datetime(2005, 4, 3, 2, tzinfo=UTC),
545- ... 'Created an attachment (id=1)')],
546- ... [(1, datetime(2005, 4, 4, tzinfo=UTC), 'First comment')],
547- ... [(1, datetime(2005, 4, 5, tzinfo=UTC), 'First comment')],
548- ... ]
549-
550- >>> attachments = [
551- ... [], [],
552- ... [(1, datetime(2005, 4, 3, 2, tzinfo=UTC), 'An attachment',
553- ... 'text/x-patch', True, 'foo.patch', 'the data', 2)],
554- ... [], []
555- ... ]
556-
557- >>> duplicates = [
558- ... (1, 2),
559- ... (3, 4),
560- ... ]
561-
562- >>> class FakeBackend:
563- ... def lookupUser(self, user_id):
564- ... return users[user_id - 1]
565- ... def getBugInfo(self, bug_id):
566- ... return buginfo[bug_id - 1]
567- ... def getBugCcs(self, bug_id):
568- ... return ccs[bug_id - 1]
569- ... def getBugComments(self, bug_id):
570- ... return comments[bug_id - 1]
571- ... def getBugAttachments(self, bug_id):
572- ... return attachments[bug_id - 1]
573- ... def getDuplicates(self):
574- ... return duplicates
575-
576- >>> from itertools import chain
577- >>> from zope.component import getUtility
578- >>> from canonical.launchpad.ftests import login
579- >>> from lp.app.interfaces.launchpad import ILaunchpadCelebrities
580- >>> from lp.bugs.interfaces.bug import IBugSet
581- >>> from lp.bugs.scripts import bugzilla
582- >>> from lp.registry.interfaces.person import IPersonSet
583+ >>> from datetime import datetime
584+ >>> import pytz
585+ >>> UTC = pytz.timezone('UTC')
586+
587+ >>> users = [
588+ ... ('test@canonical.com', 'Sample User'),
589+ ... ('foo.bar@canonical.com', 'Foo Bar'),
590+ ... ('new.user@canonical.com', 'New User') # <- not in Launchpad
591+ ... ]
592+
593+ >>> buginfo = [
594+ ... (1, # bug_id
595+ ... 1, # assigned_to
596+ ... '', # bug_file_loc
597+ ... 'normal', # bug_severity
598+ ... 'NEW', # status
599+ ... datetime(2005, 4, 1, tzinfo=UTC), # creation
600+ ... 'Test bug 1', # short_desc,
601+ ... 'Linux', # op_sys
602+ ... 'P2', # priority
603+ ... 'Ubuntu', # product
604+ ... 'AMD64', # rep_platform
605+ ... 1, # reporter
606+ ... '---', # version
607+ ... 'mozilla-firefox', # component
608+ ... '', # resolution
609+ ... 'Ubuntu 5.10', # milestone
610+ ... 0, # qa_contact
611+ ... 'status', # status_whiteboard
612+ ... '', # keywords
613+ ... ''), # alias
614+ ... # A WONTFIX bug on a non-existant distro package
615+ ... (2, 1, 'http://www.ubuntu.com', 'enhancement', 'RESOLVED',
616+ ... datetime(2005, 4, 2, tzinfo=UTC), 'Test bug 2',
617+ ... 'Linux', 'P1', 'Ubuntu', 'i386', 2, '---', 'unknown',
618+ ... 'WONTFIX', '---', 0, '', '', ''),
619+ ... # An accepted bug:
620+ ... (3, 2, 'http://www.ubuntu.com', 'blocker', 'ASSIGNED',
621+ ... datetime(2005, 4, 3, tzinfo=UTC), 'Test bug 3',
622+ ... 'Linux', 'P1', 'Ubuntu', 'i386', 1, '---', 'netapplet',
623+ ... '', '---', 0, '', '', 'xyz'),
624+ ... # A fixed bug
625+ ... (4, 1, 'http://www.ubuntu.com', 'blocker', 'CLOSED',
626+ ... datetime(2005, 4, 4, tzinfo=UTC), 'Test bug 4',
627+ ... 'Linux', 'P1', 'Ubuntu', 'i386', 1, '---', 'mozilla-firefox',
628+ ... 'FIXED', '---', 0, '', '', 'FooBar'),
629+ ... # An UPSTREAM bug
630+ ... (5, 1,
631+ ... 'http://bugzilla.gnome.org/bugs/show_bug.cgi?id=273041',
632+ ... 'blocker', 'UPSTREAM',
633+ ... datetime(2005, 4, 4, tzinfo=UTC), 'Test bug 5',
634+ ... 'Linux', 'P1', 'Ubuntu', 'i386', 1, '---', 'evolution',
635+ ... '', '---', 0, '', '', 'deb1234'),
636+ ... ]
637+
638+ >>> ccs = [[], [3], [], [], []]
639+
640+ >>> comments = [
641+ ... [(1, datetime(2005, 4, 1, tzinfo=UTC), 'First comment'),
642+ ... (2, datetime(2005, 4, 1, 1, tzinfo=UTC), 'Second comment')],
643+ ... [(1, datetime(2005, 4, 2, tzinfo=UTC), 'First comment'),
644+ ... (2, datetime(2005, 4, 2, 1, tzinfo=UTC), 'Second comment')],
645+ ... [(2, datetime(2005, 4, 3, tzinfo=UTC), 'First comment'),
646+ ... (1, datetime(2005, 4, 3, 1, tzinfo=UTC),
647+ ... 'This is related to CVE-2005-1234'),
648+ ... (2, datetime(2005, 4, 3, 2, tzinfo=UTC),
649+ ... 'Created an attachment (id=1)')],
650+ ... [(1, datetime(2005, 4, 4, tzinfo=UTC), 'First comment')],
651+ ... [(1, datetime(2005, 4, 5, tzinfo=UTC), 'First comment')],
652+ ... ]
653+
654+ >>> attachments = [
655+ ... [], [],
656+ ... [(1, datetime(2005, 4, 3, 2, tzinfo=UTC), 'An attachment',
657+ ... 'text/x-patch', True, 'foo.patch', 'the data', 2)],
658+ ... [], []
659+ ... ]
660+
661+ >>> duplicates = [
662+ ... (1, 2),
663+ ... (3, 4),
664+ ... ]
665+
666+ >>> class FakeBackend:
667+ ... def lookupUser(self, user_id):
668+ ... return users[user_id - 1]
669+ ... def getBugInfo(self, bug_id):
670+ ... return buginfo[bug_id - 1]
671+ ... def getBugCcs(self, bug_id):
672+ ... return ccs[bug_id - 1]
673+ ... def getBugComments(self, bug_id):
674+ ... return comments[bug_id - 1]
675+ ... def getBugAttachments(self, bug_id):
676+ ... return attachments[bug_id - 1]
677+ ... def getDuplicates(self):
678+ ... return duplicates
679+
680+ >>> from itertools import chain
681+ >>> from zope.component import getUtility
682+ >>> from canonical.launchpad.ftests import login
683+ >>> from lp.app.interfaces.launchpad import ILaunchpadCelebrities
684+ >>> from lp.bugs.interfaces.bug import IBugSet
685+ >>> from lp.bugs.scripts import bugzilla
686+ >>> from lp.registry.interfaces.person import IPersonSet
687
688 Get a reference to the Ubuntu bug tracker, and log in:
689
690- >>> login('bug-importer@launchpad.net')
691- >>> bugtracker = getUtility(ILaunchpadCelebrities).ubuntu_bugzilla
692+ >>> login('bug-importer@launchpad.net')
693+ >>> bugtracker = getUtility(ILaunchpadCelebrities).ubuntu_bugzilla
694
695 Now we create a bugzilla.Bugzilla instance to handle the import, using
696 our fake backend data:
697
698- >>> bz = bugzilla.Bugzilla(None)
699- >>> bz.backend = FakeBackend()
700+ >>> bz = bugzilla.Bugzilla(None)
701+ >>> bz.backend = FakeBackend()
702
703 In order to verify that things get imported correctly, the following
704 function will be used:
705
706- >>> def bugInfo(bug):
707- ... print 'Title:', bug.title
708- ... print 'Reporter:', bug.owner.displayname
709- ... print 'Created:', bug.datecreated
710- ... if bug.name:
711- ... print 'Nick: %s' % bug.name
712- ... print 'Subscribers:'
713- ... subscriber_names = sorted(
714- ... p.displayname for p in chain(
715- ... bug.getDirectSubscribers(),
716- ... bug.getIndirectSubscribers()))
717- ... for subscriber_name in subscriber_names:
718- ... print ' %s' % subscriber_name
719- ... for task in bug.bugtasks:
720- ... print 'Task:', task.bugtargetdisplayname
721- ... print ' Status:', task.status.name
722- ... if task.product:
723- ... print ' Product:', task.product.name
724- ... if task.distribution:
725- ... print ' Distro:', task.distribution.name
726- ... if task.sourcepackagename:
727- ... print ' Source package:', task.sourcepackagename.name
728- ... if task.assignee:
729- ... print ' Assignee:', task.assignee.displayname
730- ... if task.importance:
731- ... print ' Importance:', task.importance.name
732- ... if task.statusexplanation:
733- ... print ' Explanation:', task.statusexplanation
734- ... if task.milestone:
735- ... print ' Milestone:', task.milestone.name
736- ... if task.bugwatch:
737- ... print ' Watch:', task.bugwatch.url
738- ... if bug.cves:
739- ... print 'CVEs:'
740- ... for cve in bug.cves:
741- ... print ' %s' % cve.displayname
742- ... print 'Messages:'
743- ... for message in bug.messages:
744- ... print ' Author:', message.owner.displayname
745- ... print ' Date:', message.datecreated
746- ... print ' Subject:', message.subject
747- ... print ' %s' % message.text_contents
748- ... print
749- ... if bug.attachments.any():
750- ... print 'Attachments:'
751- ... for attachment in bug.attachments:
752- ... print ' Title:', attachment.title
753- ... print ' Type:', attachment.type.name
754- ... print ' Name:', attachment.libraryfile.filename
755- ... print ' Mime type:', attachment.libraryfile.mimetype
756+ >>> def bugInfo(bug):
757+ ... print 'Title:', bug.title
758+ ... print 'Reporter:', bug.owner.displayname
759+ ... print 'Created:', bug.datecreated
760+ ... if bug.name:
761+ ... print 'Nick: %s' % bug.name
762+ ... print 'Subscribers:'
763+ ... subscriber_names = sorted(
764+ ... p.displayname for p in chain(
765+ ... bug.getDirectSubscribers(),
766+ ... bug.getIndirectSubscribers()))
767+ ... for subscriber_name in subscriber_names:
768+ ... print ' %s' % subscriber_name
769+ ... for task in bug.bugtasks:
770+ ... print 'Task:', task.bugtargetdisplayname
771+ ... print ' Status:', task.status.name
772+ ... if task.product:
773+ ... print ' Product:', task.product.name
774+ ... if task.distribution:
775+ ... print ' Distro:', task.distribution.name
776+ ... if task.sourcepackagename:
777+ ... print ' Source package:', task.sourcepackagename.name
778+ ... if task.assignee:
779+ ... print ' Assignee:', task.assignee.displayname
780+ ... if task.importance:
781+ ... print ' Importance:', task.importance.name
782+ ... if task.statusexplanation:
783+ ... print ' Explanation:', task.statusexplanation
784+ ... if task.milestone:
785+ ... print ' Milestone:', task.milestone.name
786+ ... if task.bugwatch:
787+ ... print ' Watch:', task.bugwatch.url
788+ ... if bug.cves:
789+ ... print 'CVEs:'
790+ ... for cve in bug.cves:
791+ ... print ' %s' % cve.displayname
792+ ... print 'Messages:'
793+ ... for message in bug.messages:
794+ ... print ' Author:', message.owner.displayname
795+ ... print ' Date:', message.datecreated
796+ ... print ' Subject:', message.subject
797+ ... print ' %s' % message.text_contents
798+ ... print
799+ ... if bug.attachments.any():
800+ ... print 'Attachments:'
801+ ... for attachment in bug.attachments:
802+ ... print ' Title:', attachment.title
803+ ... print ' Type:', attachment.type.name
804+ ... print ' Name:', attachment.libraryfile.filename
805+ ... print ' Mime type:', attachment.libraryfile.mimetype
806
807
808 Now we import bug #1 and check the results:
809
810- >>> bug = bz.handleBug(1)
811- >>> bugInfo(bug)
812- Title: Test bug 1
813- Reporter: Sample Person
814- Created: 2005-04-01 00:00:00+00:00
815- Subscribers:
816+ >>> bug = bz.handleBug(1)
817+ >>> bugInfo(bug)
818+ Title: Test bug 1
819+ Reporter: Sample Person
820+ Created: 2005-04-01 00:00:00+00:00
821+ Subscribers:
822 Foo Bar
823 Sample Person
824 Ubuntu Team
825- Task: mozilla-firefox (Ubuntu)
826+ Task: mozilla-firefox (Ubuntu)
827 Status: NEW
828 Distro: ubuntu
829 Source package: mozilla-firefox
830 Assignee: Sample Person
831 Importance: MEDIUM
832- Explanation: status (Bugzilla status=NEW, product=Ubuntu, component=mozilla-firefox)
833+ Explanation: status (Bugzilla status=NEW, product=Ubuntu,
834+ component=mozilla-firefox)
835 Milestone: ubuntu-5.10
836- Messages:
837+ Messages:
838 Author: Sample Person
839 Date: 2005-04-01 00:00:00+00:00
840 Subject: Test bug 1
841 First comment
842- <BLANKLINE>
843+ <BLANKLINE>
844 Author: Foo Bar
845 Date: 2005-04-01 01:00:00+00:00
846 Subject: Re: Test bug 1
847 Second comment
848- <BLANKLINE>
849+ <BLANKLINE>
850
851 As well as importing the bug, a bug watch is created, linking the new
852 Launchpad bug to the original Bugzilla bug:
853
854- >>> linked_bug = getUtility(IBugSet).queryByRemoteBug(bugtracker, 1)
855- >>> linked_bug == bug
856- True
857+ >>> linked_bug = getUtility(IBugSet).queryByRemoteBug(bugtracker, 1)
858+ >>> linked_bug == bug
859+ True
860
861 This bug watch link is used to prevent multiple imports of the same
862 bug.
863
864- >>> second_import = bz.handleBug(1)
865- >>> bug == second_import
866- True
867+ >>> second_import = bz.handleBug(1)
868+ >>> bug == second_import
869+ True
870
871
872 Next we try bug #2, which is assigned to a non-existant source
873@@ -232,45 +234,48 @@
874 effect of the import, because they are subscribed to the bug.
875 * The "RESOLVED WONTFIX" status is converted to a status of INVALID.
876 * The fact that the "unknown" package does not exist in Ubuntu has
877- been logged, along with the exception raised by guessPackageNames().
878-
879- >>> print getUtility(IPersonSet).getByEmail('new.user@canonical.com')
880- None
881- >>> bug = bz.handleBug(2)
882- WARNING:lp.bugs.scripts.bugzilla:could not find package name for "unknown": 'Unknown package: unknown'
883- >>> import transaction
884- >>> transaction.commit()
885-
886- >>> bugInfo(bug)
887- Title: Test bug 2
888- Reporter: Foo Bar
889- Created: 2005-04-02 00:00:00+00:00
890- Subscribers:
891+ been logged, along with the exception raised by
892+ guessPublishedSourcePackageName().
893+
894+ >>> print getUtility(IPersonSet).getByEmail('new.user@canonical.com')
895+ None
896+ >>> bug = bz.handleBug(2)
897+ WARNING:lp.bugs.scripts.bugzilla:could not find package name for
898+ "unknown": 'Unknown package: unknown'
899+ >>> import transaction
900+ >>> transaction.commit()
901+
902+ >>> bugInfo(bug)
903+ Title: Test bug 2
904+ Reporter: Foo Bar
905+ Created: 2005-04-02 00:00:00+00:00
906+ Subscribers:
907 Foo Bar
908 New User
909 Sample Person
910 Ubuntu Team
911- Task: Ubuntu
912+ Task: Ubuntu
913 Status: INVALID
914 Distro: ubuntu
915 Assignee: Sample Person
916 Importance: WISHLIST
917- Explanation: Bugzilla status=RESOLVED WONTFIX, product=Ubuntu, component=unknown
918- Messages:
919+ Explanation: Bugzilla status=RESOLVED WONTFIX, product=Ubuntu,
920+ component=unknown
921+ Messages:
922 Author: Sample Person
923 Date: 2005-04-02 00:00:00+00:00
924 Subject: Test bug 2
925 First comment
926- <BLANKLINE>
927+ <BLANKLINE>
928 http://www.ubuntu.com
929- <BLANKLINE>
930+ <BLANKLINE>
931 Author: Foo Bar
932 Date: 2005-04-02 01:00:00+00:00
933 Subject: Re: Test bug 2
934 Second comment
935- <BLANKLINE>
936- >>> getUtility(IPersonSet).getByEmail('new.user@canonical.com')
937- <Person at ...>
938+ <BLANKLINE>
939+ >>> getUtility(IPersonSet).getByEmail('new.user@canonical.com')
940+ <Person at ...>
941
942
943 Now import an ASSIGNED bug. Things to note about this import:
944@@ -279,44 +284,45 @@
945 and CVE to be established.
946 * The attachment on this bug is imported
947
948- >>> bug = bz.handleBug(3)
949- >>> bugInfo(bug)
950- Title: Test bug 3
951- Reporter: Sample Person
952- Created: 2005-04-03 00:00:00+00:00
953- Nick: xyz
954- Subscribers:
955+ >>> bug = bz.handleBug(3)
956+ >>> bugInfo(bug)
957+ Title: Test bug 3
958+ Reporter: Sample Person
959+ Created: 2005-04-03 00:00:00+00:00
960+ Nick: xyz
961+ Subscribers:
962 Foo Bar
963 Sample Person
964 Ubuntu Team
965- Task: netapplet (Ubuntu)
966+ Task: netapplet (Ubuntu)
967 Status: CONFIRMED
968 Distro: ubuntu
969 Source package: netapplet
970 Assignee: Foo Bar
971 Importance: CRITICAL
972- Explanation: Bugzilla status=ASSIGNED, product=Ubuntu, component=netapplet
973- CVEs:
974+ Explanation: Bugzilla status=ASSIGNED, product=Ubuntu,
975+ component=netapplet
976+ CVEs:
977 CVE-2005-1234
978- Messages:
979+ Messages:
980 Author: Foo Bar
981 Date: 2005-04-03 00:00:00+00:00
982 Subject: Test bug 3
983 First comment
984- <BLANKLINE>
985+ <BLANKLINE>
986 http://www.ubuntu.com
987- <BLANKLINE>
988+ <BLANKLINE>
989 Author: Sample Person
990 Date: 2005-04-03 01:00:00+00:00
991 Subject: Re: Test bug 3
992 This is related to CVE-2005-1234
993- <BLANKLINE>
994+ <BLANKLINE>
995 Author: Foo Bar
996 Date: 2005-04-03 02:00:00+00:00
997 Subject: Re: Test bug 3
998 Created an attachment (id=1)
999- <BLANKLINE>
1000- Attachments:
1001+ <BLANKLINE>
1002+ Attachments:
1003 Title: An attachment
1004 Type: PATCH
1005 Name: foo.patch
1006@@ -325,31 +331,32 @@
1007
1008 Next we import a fixed bug:
1009
1010- >>> bug = bz.handleBug(4)
1011- >>> bugInfo(bug)
1012- Title: Test bug 4
1013- Reporter: Sample Person
1014- Created: 2005-04-04 00:00:00+00:00
1015- Nick: foobar
1016- Subscribers:
1017+ >>> bug = bz.handleBug(4)
1018+ >>> bugInfo(bug)
1019+ Title: Test bug 4
1020+ Reporter: Sample Person
1021+ Created: 2005-04-04 00:00:00+00:00
1022+ Nick: foobar
1023+ Subscribers:
1024 Foo Bar
1025 Sample Person
1026 Ubuntu Team
1027- Task: mozilla-firefox (Ubuntu)
1028+ Task: mozilla-firefox (Ubuntu)
1029 Status: FIXRELEASED
1030 Distro: ubuntu
1031 Source package: mozilla-firefox
1032 Assignee: Sample Person
1033 Importance: CRITICAL
1034- Explanation: Bugzilla status=CLOSED FIXED, product=Ubuntu, component=mozilla-firefox
1035- Messages:
1036+ Explanation: Bugzilla status=CLOSED FIXED, product=Ubuntu,
1037+ component=mozilla-firefox
1038+ Messages:
1039 Author: Sample Person
1040 Date: 2005-04-04 00:00:00+00:00
1041 Subject: Test bug 4
1042 First comment
1043- <BLANKLINE>
1044+ <BLANKLINE>
1045 http://www.ubuntu.com
1046- <BLANKLINE>
1047+ <BLANKLINE>
1048
1049
1050 The Ubuntu bugzilla uses the UPSTREAM state to categorise bugs that
1051@@ -369,40 +376,41 @@
1052 ... sourcepackagename=evolution_dsp.sourcepackagename)
1053 >>> transaction.commit()
1054
1055- >>> bug = bz.handleBug(5)
1056- >>> bugInfo(bug)
1057- Title: Test bug 5
1058- Reporter: Sample Person
1059- Created: 2005-04-04 00:00:00+00:00
1060- Subscribers:
1061+ >>> bug = bz.handleBug(5)
1062+ >>> bugInfo(bug)
1063+ Title: Test bug 5
1064+ Reporter: Sample Person
1065+ Created: 2005-04-04 00:00:00+00:00
1066+ Subscribers:
1067 Sample Person
1068 Ubuntu Team
1069- Task: Evolution
1070+ Task: Evolution
1071 Status: NEW
1072 Product: evolution
1073 Importance: UNDECIDED
1074 Watch: http://bugzilla.gnome.org/bugs/show_bug.cgi?id=273041
1075- Task: evolution (Ubuntu)
1076+ Task: evolution (Ubuntu)
1077 Status: NEW
1078 Distro: ubuntu
1079 Source package: evolution
1080 Assignee: Sample Person
1081 Importance: CRITICAL
1082- Explanation: Bugzilla status=UPSTREAM, product=Ubuntu, component=evolution
1083- Task: evolution (Debian)
1084+ Explanation: Bugzilla status=UPSTREAM, product=Ubuntu,
1085+ component=evolution
1086+ Task: evolution (Debian)
1087 Status: NEW
1088 Distro: debian
1089 Source package: evolution
1090 Importance: UNDECIDED
1091 Watch: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1234
1092- Messages:
1093+ Messages:
1094 Author: Sample Person
1095 Date: 2005-04-05 00:00:00+00:00
1096 Subject: Test bug 5
1097 First comment
1098- <BLANKLINE>
1099- http://bugzilla.gnome.org/bugs/show_bug.cgi?id=273041
1100- <BLANKLINE>
1101+ <BLANKLINE>
1102+ http://bugzilla.gnome.org/bugs/show_bug.cgi?id=273041
1103+ <BLANKLINE>
1104
1105 XXX mpt 20060404: In sampledata Evolution uses Malone officially, so adding
1106 a watch to its external bug tracker is a bad example.
1107@@ -413,47 +421,47 @@
1108
1109 Bugzilla severities are mapped to the equivalent Launchpad importance values:
1110
1111- >>> bug = bugzilla.Bug(bz.backend, 1)
1112- >>> class FakeBugTask:
1113- ... def transitionToStatus(self, status, user):
1114- ... self.status = status
1115- ... def transitionToImportance(self, importance, user):
1116- ... self.importance = importance
1117- >>> bugtask = FakeBugTask()
1118- >>> for severity in ['blocker', 'critical', 'major', 'normal',
1119- ... 'minor', 'trivial', 'enhancement']:
1120- ... bug.bug_severity = severity
1121- ... bug.mapSeverity(bugtask)
1122- ... print '%-11s %s' % (severity, bugtask.importance.name)
1123- blocker CRITICAL
1124- critical CRITICAL
1125- major HIGH
1126- normal MEDIUM
1127- minor LOW
1128- trivial LOW
1129- enhancement WISHLIST
1130+ >>> bug = bugzilla.Bug(bz.backend, 1)
1131+ >>> class FakeBugTask:
1132+ ... def transitionToStatus(self, status, user):
1133+ ... self.status = status
1134+ ... def transitionToImportance(self, importance, user):
1135+ ... self.importance = importance
1136+ >>> bugtask = FakeBugTask()
1137+ >>> for severity in ['blocker', 'critical', 'major', 'normal',
1138+ ... 'minor', 'trivial', 'enhancement']:
1139+ ... bug.bug_severity = severity
1140+ ... bug.mapSeverity(bugtask)
1141+ ... print '%-11s %s' % (severity, bugtask.importance.name)
1142+ blocker CRITICAL
1143+ critical CRITICAL
1144+ major HIGH
1145+ normal MEDIUM
1146+ minor LOW
1147+ trivial LOW
1148+ enhancement WISHLIST
1149
1150
1151 Status Mapping
1152 --------------
1153
1154- >>> for status in ['UNCONFIRMED', 'NEW', 'ASSIGNED', 'REOPENED',
1155- ... 'NEEDINFO', 'UPSTREAM', 'PENDINGUPLOAD',
1156- ... 'RESOLVED', 'VERIFIED', 'CLOSED']:
1157- ... bug.bug_status = status
1158- ... bugtask.statusexplanation = ''
1159- ... bug.mapStatus(bugtask)
1160- ... print '%-13s %s' % (status, bugtask.status.name)
1161- UNCONFIRMED NEW
1162- NEW NEW
1163- ASSIGNED CONFIRMED
1164- REOPENED NEW
1165- NEEDINFO INCOMPLETE
1166- UPSTREAM NEW
1167- PENDINGUPLOAD FIXCOMMITTED
1168- RESOLVED INVALID
1169- VERIFIED INVALID
1170- CLOSED INVALID
1171+ >>> for status in ['UNCONFIRMED', 'NEW', 'ASSIGNED', 'REOPENED',
1172+ ... 'NEEDINFO', 'UPSTREAM', 'PENDINGUPLOAD',
1173+ ... 'RESOLVED', 'VERIFIED', 'CLOSED']:
1174+ ... bug.bug_status = status
1175+ ... bugtask.statusexplanation = ''
1176+ ... bug.mapStatus(bugtask)
1177+ ... print '%-13s %s' % (status, bugtask.status.name)
1178+ UNCONFIRMED NEW
1179+ NEW NEW
1180+ ASSIGNED CONFIRMED
1181+ REOPENED NEW
1182+ NEEDINFO INCOMPLETE
1183+ UPSTREAM NEW
1184+ PENDINGUPLOAD FIXCOMMITTED
1185+ RESOLVED INVALID
1186+ VERIFIED INVALID
1187+ CLOSED INVALID
1188
1189 (note that RESOLVED, VERIFIED and CLOSED have been mapped to INVALID
1190 here because the Bugzilla resolution is set to WONTFIX).
1191@@ -461,26 +469,26 @@
1192
1193 If the bug has been resolved, the resolution will affect the status:
1194
1195- >>> bug.priority = 'P2'
1196- >>> bug.bug_status = 'RESOLVED'
1197- >>> for resolution in ['FIXED', 'INVALID', 'WONTFIX', 'NOTABUG',
1198- ... 'NOTWARTY', 'UNIVERSE', 'LATER', 'REMIND',
1199- ... 'DUPLICATE', 'WORKSFORME', 'MOVED']:
1200- ... bug.resolution = resolution
1201- ... bugtask.statusexplanation = ''
1202- ... bug.mapStatus(bugtask)
1203- ... print '%-10s %s' % (resolution, bugtask.status.name)
1204- FIXED FIXRELEASED
1205- INVALID INVALID
1206- WONTFIX INVALID
1207- NOTABUG INVALID
1208- NOTWARTY INVALID
1209- UNIVERSE INVALID
1210- LATER INVALID
1211- REMIND INVALID
1212- DUPLICATE INVALID
1213- WORKSFORME INVALID
1214- MOVED INVALID
1215+ >>> bug.priority = 'P2'
1216+ >>> bug.bug_status = 'RESOLVED'
1217+ >>> for resolution in ['FIXED', 'INVALID', 'WONTFIX', 'NOTABUG',
1218+ ... 'NOTWARTY', 'UNIVERSE', 'LATER', 'REMIND',
1219+ ... 'DUPLICATE', 'WORKSFORME', 'MOVED']:
1220+ ... bug.resolution = resolution
1221+ ... bugtask.statusexplanation = ''
1222+ ... bug.mapStatus(bugtask)
1223+ ... print '%-10s %s' % (resolution, bugtask.status.name)
1224+ FIXED FIXRELEASED
1225+ INVALID INVALID
1226+ WONTFIX INVALID
1227+ NOTABUG INVALID
1228+ NOTWARTY INVALID
1229+ UNIVERSE INVALID
1230+ LATER INVALID
1231+ REMIND INVALID
1232+ DUPLICATE INVALID
1233+ WORKSFORME INVALID
1234+ MOVED INVALID
1235
1236
1237 Bug Target Mapping
1238@@ -495,36 +503,37 @@
1239 name, the bug is targeted at that package in ubuntu. If it isn't,
1240 then the bug is filed directly against the distribution.
1241
1242- >>> def showMapping(product, component):
1243- ... bug.product = product
1244- ... bug.component = component
1245- ... target = bz.getLaunchpadBugTarget(bug)
1246- ... distribution = target.get('distribution')
1247- ... if distribution:
1248- ... print 'Distribution:', distribution.name
1249- ... spn = target.get('sourcepackagename')
1250- ... if spn:
1251- ... print 'Source package:', spn.name
1252- ... product = target.get('product')
1253- ... if product:
1254- ... print 'Product:', product.name
1255-
1256- >>> showMapping('Ubuntu', 'mozilla-firefox')
1257- Distribution: ubuntu
1258- Source package: mozilla-firefox
1259-
1260- >>> showMapping('Ubuntu', 'netapplet')
1261- Distribution: ubuntu
1262- Source package: netapplet
1263-
1264- >>> showMapping('Ubuntu', 'unknown-package-name')
1265- WARNING:lp.bugs.scripts.bugzilla:could not find package name for "unknown-package-name": 'Unknown package: unknown-package-name'
1266- Distribution: ubuntu
1267-
1268- >>> showMapping('not-Ubuntu', 'general')
1269- Traceback (most recent call last):
1270+ >>> def showMapping(product, component):
1271+ ... bug.product = product
1272+ ... bug.component = component
1273+ ... target = bz.getLaunchpadBugTarget(bug)
1274+ ... distribution = target.get('distribution')
1275+ ... if distribution:
1276+ ... print 'Distribution:', distribution.name
1277+ ... spn = target.get('sourcepackagename')
1278+ ... if spn:
1279+ ... print 'Source package:', spn.name
1280+ ... product = target.get('product')
1281+ ... if product:
1282+ ... print 'Product:', product.name
1283+
1284+ >>> showMapping('Ubuntu', 'mozilla-firefox')
1285+ Distribution: ubuntu
1286+ Source package: mozilla-firefox
1287+
1288+ >>> showMapping('Ubuntu', 'netapplet')
1289+ Distribution: ubuntu
1290+ Source package: netapplet
1291+
1292+ >>> showMapping('Ubuntu', 'unknown-package-name')
1293+ WARNING:lp.bugs.scripts.bugzilla:could not find package name for
1294+ "unknown-package-name": 'Unknown package: unknown-package-name'
1295+ Distribution: ubuntu
1296+
1297+ >>> showMapping('not-Ubuntu', 'general')
1298+ Traceback (most recent call last):
1299 ...
1300- AssertionError: product must be Ubuntu
1301+ AssertionError: product must be Ubuntu
1302
1303
1304 Duplicate Bug Handling
1305@@ -533,21 +542,21 @@
1306 The Bugzilla duplicate bugs table can be used to mark the
1307 corresponding Launchpad bugs as duplicates too:
1308
1309- >>> from lp.testing.faketransaction import FakeTransaction
1310- >>> bz.processDuplicates(FakeTransaction())
1311+ >>> from lp.testing.faketransaction import FakeTransaction
1312+ >>> bz.processDuplicates(FakeTransaction())
1313
1314 Now check that the bugs have been marked duplicate:
1315
1316- >>> bug1 = getUtility(IBugSet).queryByRemoteBug(bugtracker, 1)
1317- >>> bug2 = getUtility(IBugSet).queryByRemoteBug(bugtracker, 2)
1318- >>> bug3 = getUtility(IBugSet).queryByRemoteBug(bugtracker, 3)
1319- >>> bug4 = getUtility(IBugSet).queryByRemoteBug(bugtracker, 4)
1320+ >>> bug1 = getUtility(IBugSet).queryByRemoteBug(bugtracker, 1)
1321+ >>> bug2 = getUtility(IBugSet).queryByRemoteBug(bugtracker, 2)
1322+ >>> bug3 = getUtility(IBugSet).queryByRemoteBug(bugtracker, 3)
1323+ >>> bug4 = getUtility(IBugSet).queryByRemoteBug(bugtracker, 4)
1324
1325- >>> print bug1.duplicateof
1326- None
1327- >>> bug2.duplicateof == bug1
1328- True
1329- >>> bug3.duplicateof == None
1330- True
1331- >>> bug4.duplicateof == bug3
1332- True
1333+ >>> print bug1.duplicateof
1334+ None
1335+ >>> bug2.duplicateof == bug1
1336+ True
1337+ >>> bug3.duplicateof == None
1338+ True
1339+ >>> bug4.duplicateof == bug3
1340+ True
1341
1342=== modified file 'lib/lp/bugs/interfaces/bug.py'
1343--- lib/lp/bugs/interfaces/bug.py 2011-06-16 13:50:58 +0000
1344+++ lib/lp/bugs/interfaces/bug.py 2011-06-17 19:56:04 +0000
1345@@ -98,7 +98,7 @@
1346
1347 def __init__(self, owner, title, comment=None, description=None, msg=None,
1348 status=None, datecreated=None, security_related=False,
1349- private=False, subscribers=(), binarypackagename=None,
1350+ private=False, subscribers=(),
1351 tags=None, subscribe_owner=True, filed_by=None,
1352 importance=None, milestone=None, assignee=None):
1353 self.owner = owner
1354@@ -114,7 +114,6 @@
1355 self.product = None
1356 self.distribution = None
1357 self.sourcepackagename = None
1358- self.binarypackagename = binarypackagename
1359 self.tags = tags
1360 self.subscribe_owner = subscribe_owner
1361 self.filed_by = filed_by
1362@@ -1138,9 +1137,6 @@
1363
1364 * if either product or distribution is specified, an appropiate
1365 bug task will be created
1366-
1367- * binarypackagename, if not None, will be added to the bug's
1368- description
1369 """
1370
1371 def createBugWithoutTarget(bug_params):
1372
1373=== modified file 'lib/lp/bugs/model/bug.py'
1374--- lib/lp/bugs/model/bug.py 2011-06-16 13:50:58 +0000
1375+++ lib/lp/bugs/model/bug.py 2011-06-17 19:56:04 +0000
1376@@ -230,7 +230,7 @@
1377 bug_params, names=[
1378 "owner", "title", "comment", "description", "msg",
1379 "datecreated", "security_related", "private",
1380- "distribution", "sourcepackagename", "binarypackagename",
1381+ "distribution", "sourcepackagename",
1382 "product", "status", "subscribers", "tags",
1383 "subscribe_owner", "filed_by", "importance",
1384 "milestone", "assignee"])
1385@@ -2537,13 +2537,6 @@
1386 assert params.comment is None or params.msg is None, (
1387 "Expected either a comment or a msg, but got both.")
1388
1389- # Store binary package name in the description, because
1390- # storing it as a separate field was a maintenance burden to
1391- # developers.
1392- if params.binarypackagename:
1393- params.comment = "Binary package hint: %s\n\n%s" % (
1394- params.binarypackagename.name, params.comment)
1395-
1396 # Create the bug comment if one was given.
1397 if params.comment:
1398 rfc822msgid = make_msgid('malonedeb')
1399
1400=== modified file 'lib/lp/bugs/scripts/bugzilla.py'
1401--- lib/lp/bugs/scripts/bugzilla.py 2011-05-27 21:12:25 +0000
1402+++ lib/lp/bugs/scripts/bugzilla.py 2011-06-17 19:56:04 +0000
1403@@ -59,6 +59,7 @@
1404
1405 logger = logging.getLogger('lp.bugs.scripts.bugzilla')
1406
1407+
1408 def _add_tz(dt):
1409 """Convert a naiive datetime value to a UTC datetime value."""
1410 assert dt.tzinfo is None, 'add_tz() only accepts naiive datetime values'
1411@@ -66,6 +67,7 @@
1412 dt.hour, dt.minute, dt.second,
1413 dt.microsecond, tzinfo=pytz.timezone('UTC'))
1414
1415+
1416 class BugzillaBackend:
1417 """A wrapper for all the MySQL database access.
1418
1419@@ -208,6 +210,7 @@
1420 'ORDER BY dupe, dupe_of')
1421 return [(dupe_of, dupe) for (dupe_of, dupe) in self.cursor.fetchall()]
1422
1423+
1424 class Bug:
1425 """Representation of a Bugzilla Bug"""
1426 def __init__(self, backend, bug_id):
1427@@ -369,8 +372,8 @@
1428
1429 return person
1430
1431- def _getPackageNames(self, bug):
1432- """Returns the source and binary package names for the given bug."""
1433+ def _getPackageName(self, bug):
1434+ """Returns the source package name for the given bug."""
1435 # we currently only support mapping Ubuntu bugs ...
1436 if bug.product != 'Ubuntu':
1437 raise AssertionError('product must be Ubuntu')
1438@@ -389,19 +392,17 @@
1439 pkgname = bug.component.encode('ASCII')
1440
1441 try:
1442- srcpkg, binpkg = self.ubuntu.guessPackageNames(pkgname)
1443+ return self.ubuntu.guessPublishedSourcePackageName(pkgname)
1444 except NotFoundError, e:
1445 logger.warning('could not find package name for "%s": %s',
1446 pkgname, str(e))
1447- srcpkg = binpkg = None
1448-
1449- return srcpkg, binpkg
1450+ return None
1451
1452 def getLaunchpadBugTarget(self, bug):
1453 """Returns a dictionary of arguments to createBug() that correspond
1454 to the given bugzilla bug.
1455 """
1456- srcpkg, binpkg = self._getPackageNames(bug)
1457+ srcpkg = self._getPackageName(bug)
1458 return {
1459 'distribution': self.ubuntu,
1460 'sourcepackagename': srcpkg,
1461@@ -435,7 +436,7 @@
1462 This function relies on the package -> product linkage having been
1463 entered in advance.
1464 """
1465- srcpkgname, binpkgname = self._getPackageNames(bug)
1466+ srcpkgname = self._getPackageName(bug)
1467 # find a product series
1468 series = None
1469 for series in self.ubuntu.series:
1470@@ -450,6 +451,7 @@
1471 return None
1472
1473 _bug_re = re.compile('bug\s*#?\s*(?P<id>\d+)', re.IGNORECASE)
1474+
1475 def replaceBugRef(self, match):
1476 # XXX: jamesh 2005-10-24:
1477 # this is where bug number rewriting would be plugged in
1478@@ -457,7 +459,6 @@
1479 url = '%s/%d' % (canonical_url(self.bugtracker), bug_id)
1480 return '%s [%s]' % (match.group(0), url)
1481
1482-
1483 def handleBug(self, bug_id):
1484 """Maybe import a single bug.
1485
1486@@ -617,8 +618,10 @@
1487 * bug A' is a duplicate of bug B'
1488 * bug A is not currently a duplicate of any other bug.
1489 """
1490+
1491 logger.info('Processing duplicate bugs')
1492 bugmap = {}
1493+
1494 def getlpbug(bugid):
1495 """Get the Launchpad bug corresponding to the given remote ID
1496
1497
1498=== modified file 'lib/lp/bugs/stories/guided-filebug/xx-bug-reporting-tools.txt'
1499--- lib/lp/bugs/stories/guided-filebug/xx-bug-reporting-tools.txt 2011-04-20 12:59:55 +0000
1500+++ lib/lp/bugs/stories/guided-filebug/xx-bug-reporting-tools.txt 2011-06-17 19:56:04 +0000
1501@@ -1,4 +1,5 @@
1502-= Bug Reporting Tools =
1503+Bug Reporting Tools
1504+===================
1505
1506 In order to produce better bug reports, a bug reporting tool on the
1507 user's computer can upload a message containing extra information about
1508@@ -54,7 +55,8 @@
1509 ... job.job.complete()
1510 ... logout()
1511
1512-== Guided +filebug ==
1513+Guided +filebug
1514+===============
1515
1516 The most common case will be that the user is sent to the guided
1517 +filebug page and the user goes through the workflow there.
1518@@ -105,7 +107,8 @@
1519 >>> user_browser.getControl('Further information').value
1520 ''
1521 >>> user_browser.getControl('Submit Bug Report').click()
1522- >>> for error in find_tags_by_class(user_browser.contents, 'message error'):
1523+ >>> for error in find_tags_by_class(
1524+ ... user_browser.contents, 'message error'):
1525 ... print error.renderContents()
1526 There is 1 error.
1527
1528@@ -121,12 +124,6 @@
1529 >>> user_browser.url
1530 'http://bugs.launchpad.dev/ubuntu/+source/mozilla-firefox/+bug/...'
1531
1532-Some extra text was appended to the description.
1533-
1534- >>> find_tag_by_id(
1535- ... user_browser.contents, 'edit-description').renderContents()
1536- '...<p>Binary package hint: mozilla-firefox</p>\n<p>A bug description...'
1537-
1538 Two attachments were added.
1539
1540 >>> attachment_portlet = find_portlet(
1541@@ -149,7 +146,8 @@
1542 ----------------------------------------
1543
1544
1545-=== Initial bug summary ===
1546+Initial bug summary
1547+-------------------
1548
1549 If the uploaded message contains a Subject field in the initial headers,
1550 that will be used to automatically fill in a suggested title.
1551@@ -177,7 +175,8 @@
1552 >>> user_browser.getControl('Summary', index=0).value
1553 'Another summary'
1554
1555-=== Tags ===
1556+Tags
1557+----
1558
1559 If the uploaded message contains a Tags field, the tags widget will be
1560 initialized with that value.
1561@@ -234,7 +233,8 @@
1562 Tags: bar foo...
1563
1564
1565-=== References to HWDB submissions ===
1566+References to HWDB submissions
1567+------------------------------
1568
1569 The uploaded message may contain a header "HWDB-Submission", its value
1570 should be a sequence of HWDB submission keys, separated by ', *'.
1571
1572=== modified file 'lib/lp/bugs/xmlrpc/bug.py'
1573--- lib/lp/bugs/xmlrpc/bug.py 2010-08-20 20:31:18 +0000
1574+++ lib/lp/bugs/xmlrpc/bug.py 2011-06-17 19:56:04 +0000
1575@@ -70,7 +70,8 @@
1576
1577 if package:
1578 try:
1579- spname, bpname = distro_object.guessPackageNames(package)
1580+ spname = distro_object.guessPublishedSourcePackageName(
1581+ package)
1582 except NotFoundError:
1583 return faults.NoSuchPackage(package)
1584
1585
1586=== modified file 'lib/lp/registry/interfaces/distribution.py'
1587--- lib/lp/registry/interfaces/distribution.py 2011-06-03 09:31:08 +0000
1588+++ lib/lp/registry/interfaces/distribution.py 2011-06-17 19:56:04 +0000
1589@@ -567,14 +567,21 @@
1590 Raises NotFoundError if it fails to find the named file.
1591 """
1592
1593- def guessPackageNames(pkgname):
1594- """Try and locate source and binary package name objects that
1595- are related to the provided name -- which could be either a
1596- source or a binary package name. Returns a tuple of
1597- (sourcepackagename, binarypackagename) based on the current
1598- publishing status of these binary / source packages. Raises
1599- NotFoundError if it fails to find any package published with
1600- that name in the distribution.
1601+ def guessPublishedSourcePackageName(pkgname):
1602+ """Return the "published" SourcePackageName related to pkgname.
1603+
1604+ If pkgname corresponds to a source package that was published in
1605+ any of the distribution series, that's the SourcePackageName that is
1606+ returned.
1607+
1608+ If there is any official source package branch linked, then that
1609+ source package name is returned.
1610+
1611+ Otherwise, try to find a published binary package name and then return
1612+ the source package name from which it comes from.
1613+
1614+ :raises NotFoundError: when pkgname doesn't correspond to either a
1615+ published source or binary package name in this distribution.
1616 """
1617
1618 def getAllPPAs():
1619
1620=== modified file 'lib/lp/registry/model/distribution.py'
1621--- lib/lp/registry/model/distribution.py 2011-06-08 23:31:41 +0000
1622+++ lib/lp/registry/model/distribution.py 2011-06-17 19:56:04 +0000
1623@@ -119,10 +119,12 @@
1624 HasBugHeatMixin,
1625 OfficialBugTagTargetMixin,
1626 )
1627-from lp.bugs.model.bugtask import BugTask
1628 from lp.bugs.model.structuralsubscription import (
1629 StructuralSubscriptionTargetMixin,
1630 )
1631+from lp.code.interfaces.seriessourcepackagebranch import (
1632+ IFindOfficialBranchLinks,
1633+ )
1634 from lp.registry.errors import NoSuchDistroSeries
1635 from lp.registry.interfaces.distribution import (
1636 IBaseDistribution,
1637@@ -649,7 +651,8 @@
1638 """See `IBugTarget`."""
1639 return get_bug_tags("BugTask.distribution = %s" % sqlvalues(self))
1640
1641- def getUsedBugTagsWithOpenCounts(self, user, tag_limit=0, include_tags=None):
1642+ def getUsedBugTagsWithOpenCounts(self, user, tag_limit=0,
1643+ include_tags=None):
1644 """See IBugTarget."""
1645 # Circular fail.
1646 from lp.bugs.model.bugsummary import BugSummary
1647@@ -1370,7 +1373,7 @@
1648 # results will only see DSPCs
1649 return DecoratedResultSet(results, result_to_dspc)
1650
1651- def guessPackageNames(self, pkgname):
1652+ def guessPublishedSourcePackageName(self, pkgname):
1653 """See `IDistribution`"""
1654 assert isinstance(pkgname, basestring), (
1655 "Expected string. Got: %r" % pkgname)
1656@@ -1386,78 +1389,34 @@
1657 'published in it'
1658 % (self.displayname, pkgname))
1659
1660- # The way this method works is that is tries to locate a pair
1661- # of packages related to that name. If it locates a source
1662- # package it then tries to see if it has been published at any
1663- # point, and gets the binary package from the publishing
1664- # record.
1665- #
1666- # If that fails (no source package by that name, or not
1667- # published) then it'll search binary packages, then find the
1668- # source package most recently associated with it, first in
1669- # the current distroseries and then across the whole
1670- # distribution.
1671- #
1672- # XXX kiko 2006-07-28:
1673- # Note that the strategy of falling back to previous
1674- # distribution series might be revisited in the future; for
1675- # instance, when people file bugs, it might actually be bad for
1676- # us to allow them to be associated with obsolete packages.
1677-
1678- bpph_location_clauses = [
1679- DistroSeries.distribution == self,
1680- DistroArchSeries.distroseriesID == DistroSeries.id,
1681- BinaryPackagePublishingHistory.distroarchseriesID ==
1682- DistroArchSeries.id,
1683- BinaryPackagePublishingHistory.archiveID.is_in(
1684- self.all_distro_archive_ids),
1685- BinaryPackagePublishingHistory.dateremoved == None,
1686- BinaryPackageRelease.id ==
1687- BinaryPackagePublishingHistory.binarypackagereleaseID,
1688- ]
1689-
1690 sourcepackagename = SourcePackageName.selectOneBy(name=pkgname)
1691 if sourcepackagename:
1692 # Note that in the source package case, we don't restrict
1693 # the search to the distribution release, making a best
1694 # effort to find a package.
1695- publishing = SourcePackagePublishingHistory.selectFirst('''
1696- SourcePackagePublishingHistory.distroseries =
1697- DistroSeries.id AND
1698- DistroSeries.distribution = %s AND
1699- SourcePackagePublishingHistory.archive IN %s AND
1700- SourcePackagePublishingHistory.sourcepackagerelease =
1701- SourcePackageRelease.id AND
1702- SourcePackageRelease.sourcepackagename = %s AND
1703- SourcePackagePublishingHistory.status IN %s
1704- ''' % sqlvalues(self,
1705- self.all_distro_archive_ids,
1706- sourcepackagename,
1707- (PackagePublishingStatus.PUBLISHED,
1708- PackagePublishingStatus.PENDING)),
1709- clauseTables=['SourcePackageRelease', 'DistroSeries'],
1710- distinct=True,
1711- orderBy="id")
1712+ publishing = IStore(SourcePackagePublishingHistory).find(
1713+ SourcePackagePublishingHistory,
1714+ SourcePackagePublishingHistory.archiveID == Archive.id,
1715+ Archive.distribution == self,
1716+ Archive.purpose.is_in(MAIN_ARCHIVE_PURPOSES),
1717+ SourcePackagePublishingHistory.sourcepackagereleaseID ==
1718+ SourcePackageRelease.id,
1719+ SourcePackageRelease.sourcepackagename == sourcepackagename,
1720+ SourcePackagePublishingHistory.status.is_in(
1721+ (PackagePublishingStatus.PUBLISHED,
1722+ PackagePublishingStatus.PENDING)
1723+ )).order_by(
1724+ Desc(SourcePackagePublishingHistory.id)).first()
1725 if publishing is not None:
1726- # Attempt to find a published binary package of the
1727- # same name.
1728- bpph = IStore(BinaryPackagePublishingHistory).find(
1729- BinaryPackagePublishingHistory,
1730- BinaryPackageRelease.binarypackagename ==
1731- BinaryPackageName.id,
1732- BinaryPackageName.name == sourcepackagename.name,
1733- BinaryPackageBuild.id == BinaryPackageRelease.buildID,
1734- SourcePackageRelease.id ==
1735- BinaryPackageBuild.source_package_release_id,
1736- SourcePackageRelease.sourcepackagename ==
1737- sourcepackagename,
1738- *bpph_location_clauses).any()
1739- if bpph is not None:
1740- bpr = bpph.binarypackagerelease
1741- return (sourcepackagename, bpr.binarypackagename)
1742- # No binary with a similar name, so just return None
1743- # rather than returning some arbitrary binary package.
1744- return (sourcepackagename, None)
1745+ return sourcepackagename
1746+
1747+ # Look to see if there is an official source package branch.
1748+ # That's considered "published" enough.
1749+ branch_links = getUtility(IFindOfficialBranchLinks)
1750+ results = branch_links.findForDistributionSourcePackage(
1751+ self.getSourcePackage(sourcepackagename))
1752+ if results.any() is not None:
1753+ return sourcepackagename
1754
1755 # At this point we don't have a published source package by
1756 # that name, so let's try to find a binary package and work
1757@@ -1470,12 +1429,18 @@
1758 # the sourcepackagename from that.
1759 bpph = IStore(BinaryPackagePublishingHistory).find(
1760 BinaryPackagePublishingHistory,
1761+ BinaryPackagePublishingHistory.archiveID == Archive.id,
1762+ Archive.distribution == self,
1763+ Archive.purpose.is_in(MAIN_ARCHIVE_PURPOSES),
1764+ BinaryPackagePublishingHistory.binarypackagereleaseID ==
1765+ BinaryPackageRelease.id,
1766 BinaryPackageRelease.binarypackagename == binarypackagename,
1767- *bpph_location_clauses).order_by(
1768+ BinaryPackagePublishingHistory.dateremoved == None,
1769+ ).order_by(
1770 Desc(BinaryPackagePublishingHistory.id)).first()
1771 if bpph is not None:
1772 spr = bpph.binarypackagerelease.build.source_package_release
1773- return (spr.sourcepackagename, binarypackagename)
1774+ return spr.sourcepackagename
1775
1776 # We got nothing so signal an error.
1777 if sourcepackagename is None:
1778
1779=== modified file 'lib/lp/registry/tests/test_distribution.py'
1780--- lib/lp/registry/tests/test_distribution.py 2011-05-14 15:02:13 +0000
1781+++ lib/lp/registry/tests/test_distribution.py 2011-06-17 19:56:04 +0000
1782@@ -7,6 +7,7 @@
1783
1784 from lazr.lifecycle.snapshot import Snapshot
1785 import soupmatchers
1786+from testtools import ExpectedException
1787 from testtools.matchers import (
1788 MatchesAny,
1789 Not,
1790@@ -14,11 +15,13 @@
1791 from zope.component import getUtility
1792 from zope.security.proxy import removeSecurityProxy
1793
1794+from canonical.database.constants import UTC_NOW
1795 from canonical.launchpad.webapp import canonical_url
1796 from canonical.testing.layers import (
1797 DatabaseFunctionalLayer,
1798 LaunchpadFunctionalLayer,
1799 )
1800+from lp.app.errors import NotFoundError
1801 from lp.registry.errors import NoSuchDistroSeries
1802 from lp.registry.interfaces.distribution import IDistribution
1803 from lp.registry.interfaces.person import IPersonSet
1804@@ -41,9 +44,6 @@
1805
1806 layer = DatabaseFunctionalLayer
1807
1808- def setUp(self):
1809- super(TestDistribution, self).setUp('foo.bar@canonical.com')
1810-
1811 def test_distribution_repr_ansii(self):
1812 # Verify that ANSI displayname is ascii safe.
1813 distro = self.factory.makeDistribution(
1814@@ -59,6 +59,127 @@
1815 ignore, displayname, name = repr(distro).rsplit(' ', 2)
1816 self.assertEqual("'\\u0170-distro'", displayname)
1817
1818+ def test_guessPublishedSourcePackageName_no_distro_series(self):
1819+ # Distribution without a series raises NotFoundError
1820+ distro = self.factory.makeDistribution()
1821+ with ExpectedException(NotFoundError, '.*has no series.*'):
1822+ distro.guessPublishedSourcePackageName('package')
1823+
1824+ def test_guessPublishedSourcePackageName_invalid_name(self):
1825+ # Invalid name raises a NotFoundError
1826+ distro = self.factory.makeDistribution()
1827+ with ExpectedException(NotFoundError, "'Invalid package name.*"):
1828+ distro.guessPublishedSourcePackageName('a*package')
1829+
1830+ def test_guessPublishedSourcePackageName_nothing_published(self):
1831+ distroseries = self.factory.makeDistroSeries()
1832+ with ExpectedException(NotFoundError, "'Unknown package:.*"):
1833+ distroseries.distribution.guessPublishedSourcePackageName(
1834+ 'a-package')
1835+
1836+ def test_guessPublishedSourcePackageName_ignored_removed(self):
1837+ # Removed binary package are ignored.
1838+ distroseries = self.factory.makeDistroSeries()
1839+ self.factory.makeBinaryPackagePublishingHistory(
1840+ archive=distroseries.main_archive,
1841+ binarypackagename='binary-package', dateremoved=UTC_NOW)
1842+ with ExpectedException(NotFoundError, ".*Binary package.*"):
1843+ distroseries.distribution.guessPublishedSourcePackageName(
1844+ 'binary-package')
1845+
1846+ def test_guessPublishedSourcePackageName_sourcepackage_name(self):
1847+ distroseries = self.factory.makeDistroSeries()
1848+ spph = self.factory.makeSourcePackagePublishingHistory(
1849+ distroseries=distroseries, sourcepackagename='my-package')
1850+ self.assertEquals(
1851+ spph.sourcepackagerelease.sourcepackagename,
1852+ distroseries.distribution.guessPublishedSourcePackageName(
1853+ 'my-package'))
1854+
1855+ def test_guessPublishedSourcePackageName_binarypackage_name(self):
1856+ distroseries = self.factory.makeDistroSeries()
1857+ spph = self.factory.makeSourcePackagePublishingHistory(
1858+ distroseries=distroseries, sourcepackagename='my-package')
1859+ self.factory.makeBinaryPackagePublishingHistory(
1860+ archive=distroseries.main_archive,
1861+ binarypackagename='binary-package',
1862+ source_package_release=spph.sourcepackagerelease)
1863+ self.assertEquals(
1864+ spph.sourcepackagerelease.sourcepackagename,
1865+ distroseries.distribution.guessPublishedSourcePackageName(
1866+ 'binary-package'))
1867+
1868+ def test_guessPublishedSourcePackageName_exlude_ppa(self):
1869+ # Package published in PPAs are not considered to be part of the
1870+ # distribution.
1871+ distroseries = self.factory.makeUbuntuDistroSeries()
1872+ ppa_archive = self.factory.makeArchive()
1873+ self.factory.makeSourcePackagePublishingHistory(
1874+ distroseries=distroseries, sourcepackagename='my-package',
1875+ archive=ppa_archive)
1876+ with ExpectedException(NotFoundError, ".*not published in.*"):
1877+ distroseries.distribution.guessPublishedSourcePackageName(
1878+ 'my-package')
1879+
1880+ def test_guessPublishedSourcePackageName_exlude_other_distro(self):
1881+ # Published source package are only found in the distro
1882+ # in which they were published.
1883+ distroseries1 = self.factory.makeDistroSeries()
1884+ distroseries2 = self.factory.makeDistroSeries()
1885+ spph = self.factory.makeSourcePackagePublishingHistory(
1886+ distroseries=distroseries1, sourcepackagename='my-package')
1887+ self.assertEquals(
1888+ spph.sourcepackagerelease.sourcepackagename,
1889+ distroseries1.distribution.guessPublishedSourcePackageName(
1890+ 'my-package'))
1891+ with ExpectedException(NotFoundError, ".*not published in.*"):
1892+ distroseries2.distribution.guessPublishedSourcePackageName(
1893+ 'my-package')
1894+
1895+ def test_guessPublishedSourcePackageName_looks_for_source_first(self):
1896+ # If both a binary and source package name shares the same name,
1897+ # the source package will be returned (and the one from the unrelated
1898+ # binary).
1899+ distroseries = self.factory.makeDistroSeries()
1900+ my_spph = self.factory.makeSourcePackagePublishingHistory(
1901+ distroseries=distroseries, sourcepackagename='my-package')
1902+ self.factory.makeBinaryPackagePublishingHistory(
1903+ archive=distroseries.main_archive,
1904+ binarypackagename='my-package', sourcepackagename='other-package')
1905+ self.assertEquals(
1906+ my_spph.sourcepackagerelease.sourcepackagename,
1907+ distroseries.distribution.guessPublishedSourcePackageName(
1908+ 'my-package'))
1909+
1910+ def test_guessPublishedSourcePackageName_uses_latest(self):
1911+ # If multiple binaries match, it will return the source of the latest
1912+ # one published.
1913+ distroseries = self.factory.makeDistroSeries()
1914+ self.factory.makeBinaryPackagePublishingHistory(
1915+ archive=distroseries.main_archive,
1916+ sourcepackagename='old-source-name',
1917+ binarypackagename='my-package')
1918+ self.factory.makeBinaryPackagePublishingHistory(
1919+ archive=distroseries.main_archive,
1920+ sourcepackagename='new-source-name',
1921+ binarypackagename='my-package')
1922+ self.assertEquals(
1923+ 'new-source-name',
1924+ distroseries.distribution.guessPublishedSourcePackageName(
1925+ 'my-package').name)
1926+
1927+ def test_guessPublishedSourcePackageName_official_package_branch(self):
1928+ # It consider that a sourcepackage that has an official package
1929+ # branch is published.
1930+ sourcepackage = self.factory.makeSourcePackage(
1931+ sourcepackagename='my-package')
1932+ self.factory.makeRelatedBranchesForSourcePackage(
1933+ sourcepackage=sourcepackage)
1934+ self.assertEquals(
1935+ 'my-package',
1936+ sourcepackage.distribution.guessPublishedSourcePackageName(
1937+ 'my-package').name)
1938+
1939
1940 class TestDistributionCurrentSourceReleases(
1941 TestDistroSeriesCurrentSourceReleases):
1942
1943=== modified file 'lib/lp/soyuz/doc/distribution.txt'
1944--- lib/lp/soyuz/doc/distribution.txt 2011-05-27 19:53:20 +0000
1945+++ lib/lp/soyuz/doc/distribution.txt 2011-06-17 19:56:04 +0000
1946@@ -5,62 +5,17 @@
1947 objects for the distribution.
1948
1949
1950-Guessing package names
1951-----------------------
1952-
1953-IDistribution allows us to retrieve packages by name, returning a tuple
1954-of Source/BinaryPackageName instances published within this
1955-distribution:
1956-
1957 >>> from lp.registry.interfaces.distribution import IDistributionSet
1958 >>> from lp.registry.interfaces.pocket import PackagePublishingPocket
1959- >>> from lp.registry.interfaces.sourcepackagename import ISourcePackageName
1960 >>> from lp.soyuz.enums import PackagePublishingStatus
1961- >>> from lp.soyuz.interfaces.binarypackagename import IBinaryPackageName
1962-
1963- >>> distroset = getUtility(IDistributionSet)
1964- >>> gentoo = distroset.getByName("gentoo")
1965- >>> ubuntu = distroset.get(1)
1966-
1967- >>> source_name, bin_name = ubuntu.guessPackageNames('pmount')
1968- >>> ISourcePackageName.providedBy(source_name)
1969- True
1970-
1971- >>> IBinaryPackageName.providedBy(bin_name)
1972- True
1973-
1974- >>> source_name.name, bin_name.name
1975- (u'pmount', u'pmount')
1976-
1977-Prevents wrong usage by and assertion error:
1978-
1979- >>> name_tuple = ubuntu.guessPackageNames(ubuntu)
1980- Traceback (most recent call last):
1981- ...
1982- AssertionError: Expected string. Got: <Distribution ...>
1983-
1984-Raises NotFoundError for following conditions:
1985-
1986- >>> name_tuple = ubuntu.guessPackageNames('@#$')
1987- Traceback (most recent call last):
1988- ...
1989- NotFoundError: 'Invalid package name: @#$'
1990-
1991- >>> name_tuple = ubuntu.guessPackageNames('zeca')
1992- Traceback (most recent call last):
1993- ...
1994- NotFoundError: 'Unknown package: zeca'
1995-
1996- >>> name_tuple = ubuntu.guessPackageNames('1234')
1997- Traceback (most recent call last):
1998- ...
1999- NotFoundError: 'Unknown package: 1234'
2000-
2001-Packages only published in PPAs will not be found in the Ubuntu archive.
2002-Here, 'at' is published in mark's PPA only:
2003+
2004+ >>> ubuntu = getUtility(IDistributionSet).getByName('ubuntu')
2005+ >>> debian = getUtility(IDistributionSet).getByName('debian')
2006+
2007+(Create some data that is depended upon by later tests. It was part of a
2008+test "narrative" that was converted to unit tests.... for obvious reasons.)
2009
2010 >>> from lp.soyuz.tests.ppa import publishToPPA
2011- >>> ubuntutest = distroset.getByName("ubuntutest")
2012 >>> publishToPPA(
2013 ... person_name='cprov',
2014 ... sourcepackage_name='at', sourcepackage_version='0.00',
2015@@ -68,131 +23,6 @@
2016 ... distribution_name='ubuntutest',
2017 ... distroseries_name='hoary-test',
2018 ... publishing_status=PackagePublishingStatus.PUBLISHED)
2019- >>> name_tuple = ubuntutest.guessPackageNames('at')
2020- Traceback (most recent call last):
2021- ...
2022- NotFoundError: u'Package at not published in ubuntutest'
2023-
2024-It also raises NotFoundError on distributions with no series:
2025-
2026- >>> source_name, bin_name = gentoo.guessPackageNames('pmount')
2027- Traceback (most recent call last):
2028- ...
2029- NotFoundError: u"Gentoo has no series; 'pmount' was never published in it"
2030-
2031-A distroseries can only be created by the distro owner or the admin
2032-team.
2033-
2034- >>> gentoo.newSeries('gentoo-two', 'Gentoo Two',
2035- ... 'Gentoo Two Dot Oh', 'Gentoo 2', 'G2',
2036- ... '2.0', None, gentoo.owner)
2037- Traceback (most recent call last):
2038- ...
2039- Unauthorized: (<Distribution...>, 'newSeries', 'launchpad.Moderate')
2040-
2041- >>> login('mark@example.com')
2042- >>> from lp.registry.interfaces.distroseries import IDistroSeriesSet
2043- >>> distroseriesset = getUtility(IDistroSeriesSet)
2044- >>> gentoo_two = gentoo.newSeries('gentoo-two', 'Gentoo Two',
2045- ... 'Gentoo Two Dot Oh', 'Gentoo 2', 'G2',
2046- ... '2.0', None, gentoo.owner)
2047-
2048- # Reverting the logged in user.
2049-
2050- >>> login(ANONYMOUS)
2051-
2052-Even if we add a series to Gentoo, no packages have ever been published
2053-in it, and therefore guessPackageNames will still fail:
2054-
2055- >>> source_name, bin_name = gentoo.guessPackageNames('pmount')
2056- Traceback (most recent call last):
2057- ...
2058- NotFoundError: u'Package pmount not published in Gentoo'
2059-
2060-It will find packages that are at the PENDING publishing state in
2061-addition to PUBLISHED ones:
2062-
2063- >>> login("admin@canonical.com")
2064- >>> from lp.soyuz.tests.test_publishing import (
2065- ... SoyuzTestPublisher)
2066- >>> test_publisher = SoyuzTestPublisher()
2067- >>> ignore = test_publisher.setUpDefaultDistroSeries(
2068- ... ubuntu['breezy-autotest'])
2069- >>> ignore = test_publisher.getPubSource(
2070- ... sourcename="pendingpackage",
2071- ... status=PackagePublishingStatus.PENDING)
2072- >>> login(ANONYMOUS)
2073- >>> (source, binary) = ubuntu.guessPackageNames("pendingpackage")
2074- >>> print source.name
2075- pendingpackage
2076-
2077-It also works if we look for a package name which is the name of both
2078-binary and source packages but for which only the source is published:
2079-
2080- >>> from lp.app.interfaces.launchpad import ILaunchpadCelebrities
2081- >>> debian = getUtility(ILaunchpadCelebrities).debian
2082- >>> source_name, bin_name = debian.guessPackageNames('alsa-utils')
2083- >>> print bin_name
2084- None
2085-
2086- >>> source_name.name
2087- u'alsa-utils'
2088-
2089-It's possible for a binary package to have the same name as a source
2090-package, yet not be derived from that source package. In this case, we
2091-want to prefer the source package with that name.
2092-
2093-First, we need a function to help testing:
2094-
2095- >>> def print_guessed_names(package_name):
2096- ... source, binary = ubuntu.guessPackageNames(package_name)
2097- ... print "source: %r" % source.name
2098- ... print "binary: %r" % getattr(binary, 'name', None)
2099-
2100-Note that source packages can produces lots of differently named binary
2101-packages so only return a match if it's got the same name as the source
2102-package rather than returning an arbitrary binary package:
2103-
2104-Both iceweasel and mozilla-firefox source packages produce mozilla-
2105-firefox binary packages.
2106-
2107- >>> print_guessed_names('mozilla-firefox')
2108- source: u'mozilla-firefox'
2109- binary: u'mozilla-firefox'
2110-
2111- >>> print_guessed_names('iceweasel')
2112- source: u'iceweasel'
2113- binary: None
2114-
2115-If we don't get a hit on the source package we search binary packages.
2116-Because there is a many to one relationship from binary packages to
2117-source packages we can always return a source package name even if it
2118-differs:
2119-
2120- >>> print_guessed_names('linux-2.6.12')
2121- source: u'linux-source-2.6.15'
2122- binary: u'linux-2.6.12'
2123-
2124-If there are multiple matching binary packages, the source of the latest
2125-publication is used. If we create a new 'linux' source with a 'linux-2.6.12'
2126-binary, 'linux' will be returned instead of 'linux-source-2.6.15'.
2127-
2128- >>> from lp.soyuz.tests.test_publishing import (
2129- ... SoyuzTestPublisher)
2130- >>> test_publisher = SoyuzTestPublisher()
2131- >>> hoary = test_publisher.setUpDefaultDistroSeries(
2132- ... ubuntu.getSeries('hoary'))
2133- >>> fake_chroot = test_publisher.addMockFile('fake_chroot.tar.gz')
2134- >>> unused = hoary['i386'].addOrUpdateChroot(fake_chroot)
2135- >>> login('admin@canonical.com')
2136- >>> test_publisher.getPubBinaries(
2137- ... 'linux-2.6.12', architecturespecific=True)
2138- [<BinaryPackagePublishingHistory ...>]
2139- >>> login(ANONYMOUS)
2140-
2141- >>> print_guessed_names('linux-2.6.12')
2142- source: u'linux'
2143- binary: u'linux-2.6.12'
2144
2145
2146 Handling Personal Package Archives
2147
2148=== modified file 'lib/lp/testing/factory.py'
2149--- lib/lp/testing/factory.py 2011-06-14 08:25:41 +0000
2150+++ lib/lp/testing/factory.py 2011-06-17 19:56:04 +0000
2151@@ -3486,8 +3486,10 @@
2152 distribution=distroseries.distribution,
2153 purpose=ArchivePurpose.PRIMARY)
2154
2155- if sourcepackagename is None:
2156- sourcepackagename = self.makeSourcePackageName()
2157+ if (sourcepackagename is None or
2158+ isinstance(sourcepackagename, basestring)):
2159+ sourcepackagename = self.getOrMakeSourcePackageName(
2160+ sourcepackagename)
2161
2162 if component is None:
2163 component = self.makeComponent()
2164@@ -3554,13 +3556,16 @@
2165
2166 def makeBinaryPackageBuild(self, source_package_release=None,
2167 distroarchseries=None, archive=None, builder=None,
2168- status=None, pocket=None, date_created=None, processor=None):
2169+ status=None, pocket=None, date_created=None, processor=None,
2170+ sourcepackagename=None):
2171 """Create a BinaryPackageBuild.
2172
2173 If archive is not supplied, the source_package_release is used
2174 to determine archive.
2175 :param source_package_release: The SourcePackageRelease this binary
2176 build uses as its source.
2177+ :param sourcepackagename: when source_package_release is None, the
2178+ sourcepackagename from which the build will come.
2179 :param distroarchseries: The DistroArchSeries to use.
2180 :param archive: The Archive to use.
2181 :param builder: An optional builder to assign.
2182@@ -3587,7 +3592,8 @@
2183 multiverse = self.makeComponent(name='multiverse')
2184 source_package_release = self.makeSourcePackageRelease(
2185 archive, component=multiverse,
2186- distroseries=distroarchseries.distroseries)
2187+ distroseries=distroarchseries.distroseries,
2188+ sourcepackagename=sourcepackagename)
2189 self.makeSourcePackagePublishingHistory(
2190 distroseries=source_package_release.upload_distroseries,
2191 archive=archive, sourcepackagerelease=source_package_release,
2192@@ -3685,12 +3691,15 @@
2193 return spph
2194
2195 def makeBinaryPackagePublishingHistory(self, binarypackagerelease=None,
2196+ binarypackagename=None,
2197 distroarchseries=None,
2198 component=None, section_name=None,
2199 priority=None, status=None,
2200 scheduleddeletiondate=None,
2201 dateremoved=None,
2202- pocket=None, archive=None):
2203+ pocket=None, archive=None,
2204+ source_package_release=None,
2205+ sourcepackagename=None):
2206 """Make a `BinaryPackagePublishingHistory`."""
2207 if distroarchseries is None:
2208 if archive is None:
2209@@ -3720,8 +3729,10 @@
2210 # in the same archive and suite.
2211 binarypackagebuild = self.makeBinaryPackageBuild(
2212 archive=archive, distroarchseries=distroarchseries,
2213- pocket=pocket)
2214+ pocket=pocket, source_package_release=source_package_release,
2215+ sourcepackagename=sourcepackagename)
2216 binarypackagerelease = self.makeBinaryPackageRelease(
2217+ binarypackagename=binarypackagename,
2218 build=binarypackagebuild,
2219 component=component,
2220 section_name=section_name,
2221@@ -3785,8 +3796,10 @@
2222 """Make a `BinaryPackageRelease`."""
2223 if build is None:
2224 build = self.makeBinaryPackageBuild()
2225- if binarypackagename is None:
2226- binarypackagename = self.makeBinaryPackageName()
2227+ if (binarypackagename is None or
2228+ isinstance(binarypackagename, basestring)):
2229+ binarypackagename = self.getOrMakeBinaryPackageName(
2230+ binarypackagename)
2231 if version is None:
2232 version = build.source_package_release.version
2233 if binpackageformat is None: