apport-hackers team mailing list archive
-
apport-hackers team
-
Mailing list archive
-
Message #00015
[Merge] lp:~matsubara/apport/no-more-screenscraping into lp:apport
Diogo Matsubara has proposed merging lp:~matsubara/apport/no-more-screenscraping into lp:apport.
Requested reviews:
Apport upstream developers (apport-hackers)
Hi Martin,
this branch changes apport/crashdb_impl/launchpad.py to use launchpadlib rather than screenscraping to handle the file bug workflow. It also provides a way to run the launchpad tests against the development launchpad instance (launchpad.dev). When I run the tests against the launchpad.dev instance I get only one test failure, which I hope you can help me sort out before landing this code.
To run the tests:
APPORT_LAUNCHPAD_INSTANCE=dev python -tt apport/crashdb_impl/launchpad.py
Let me know what you think and what changes are necessary to make this branch landable on apport trunk.
Thanks!
--
https://code.launchpad.net/~matsubara/apport/no-more-screenscraping/+merge/34494
Your team Apport upstream developers is requested to review the proposed merge of lp:~matsubara/apport/no-more-screenscraping into lp:apport.
=== modified file 'apport/crashdb_impl/launchpad.py'
--- apport/crashdb_impl/launchpad.py 2010-06-16 11:03:07 +0000
+++ apport/crashdb_impl/launchpad.py 2010-09-02 21:45:52 +0000
@@ -10,12 +10,12 @@
# option) any later version. See http://www.gnu.org/copyleft/gpl.html for
# the full text of the license.
-import urllib, tempfile, shutil, os.path, re, gzip, sys, socket, ConfigParser
+import urllib, tempfile, os.path, re, gzip, sys
import email
from cStringIO import StringIO
from launchpadlib.errors import HTTPError
-from launchpadlib.launchpad import Launchpad, STAGING_SERVICE_ROOT, EDGE_SERVICE_ROOT
+from launchpadlib.launchpad import Launchpad
import apport.crashdb
import apport
@@ -26,7 +26,7 @@
for attachment in attachments:
try:
f = attachment.data.open()
- except HTTPError, e:
+ except HTTPError:
print >> sys.stderr, 'ERROR: Broken attachment on bug, ignoring'
continue
name = f.filename
@@ -52,8 +52,9 @@
- distro: Name of the distribution in Launchpad
- project: Name of the project in Launchpad
(Note that exactly one of "distro" or "project" must be given.)
- - staging: If set, this uses staging instead of production (optional).
- This can be overriden or set by $APPORT_STAGING environment.
+ - launchpad_instance: If set, this uses the given launchpad instance
+ instead of production (optional). This can be overriden or set by
+ $APPORT_LAUNCHPAD_INSTANCE environment.
- cache_dir: Path to a permanent cache directory; by default it uses a
temporary one. (optional). This can be overridden or set by
$APPORT_LAUNCHPAD_CACHE environment.
@@ -62,11 +63,13 @@
- escalation_tag: This adds the given tag to a bug once it gets more
than 10 duplicates.
'''
- if os.getenv('APPORT_STAGING'):
- options['staging'] = True
+ if os.getenv('APPORT_LAUNCHPAD_INSTANCE'):
+ options['launchpad_instance'] = os.getenv(
+ 'APPORT_LAUNCHPAD_INSTANCE')
if not auth:
- if options.get('staging'):
- auth = default_credentials_path + '.staging'
+ lp_instance = options.get('launchpad_instance')
+ if lp_instance:
+ auth = default_credentials_path + '.' + lp_instance
else:
auth = default_credentials_path
apport.crashdb.CrashDatabase.__init__(self, auth,
@@ -94,10 +97,10 @@
if self.__launchpad:
return self.__launchpad
- if self.options.get('staging'):
- launchpad_instance = STAGING_SERVICE_ROOT
+ if self.options.get('launchpad_instance'):
+ launchpad_instance = self.options.get('launchpad_instance')
else:
- launchpad_instance = EDGE_SERVICE_ROOT
+ launchpad_instance = 'edge'
auth_dir = os.path.dirname(self.auth)
if auth_dir and not os.path.isdir(auth_dir):
@@ -184,11 +187,35 @@
mime.flush()
mime.seek(0)
- ticket = upload_blob(mime, progress_callback,
- staging=self.options.get('staging', False))
+ ticket = upload_blob(
+ mime, progress_callback, hostname=self.get_hostname())
assert ticket
return ticket
+ def get_target(self, report):
+ """Return the bug_target for this report."""
+ project = self.options.get('project')
+ if report.has_key('SourcePackage'):
+ bug_target = self.lp_distro.getSourcePackage(
+ name=report['SourcePackage'])
+ elif project:
+ bug_target = self.launchpad.projects[project]
+ else:
+ bug_target = self.lp_distro
+ return bug_target
+
+ def get_hostname(self):
+ """Return the hostname for the Launchpad instance."""
+ launchpad_instance = self.options.get('launchpad_instance')
+ if launchpad_instance:
+ if launchpad_instance == 'staging':
+ hostname = 'staging.launchpad.net'
+ else:
+ hostname = 'launchpad.dev'
+ else:
+ hostname = 'launchpad.net'
+ return hostname
+
def get_comment_url(self, report, handle):
'''Return an URL that should be opened after report has been uploaded
and upload() returned handle.
@@ -202,13 +229,10 @@
if title:
args['field.title'] = title
- if self.options.get('staging'):
- hostname = 'staging.launchpad.net'
- else:
- hostname = 'launchpad.net'
+ hostname = self.get_hostname()
project = self.options.get('project')
-
+
if not project:
if report.has_key('SourcePackage'):
return 'https://bugs.%s/%s/+source/%s/+filebug/%s?%s' % (
@@ -780,7 +804,7 @@
def https_open(self, req):
return self.do_open(HTTPSProgressConnection, req)
-def upload_blob(blob, progress_callback = None, staging=False):
+def upload_blob(blob, progress_callback = None, hostname='launchpad.net'):
'''Upload blob (file-like object) to Launchpad.
progress_callback can be set to a function(sent, total) which is regularly
@@ -790,19 +814,20 @@
Return None on error, or the ticket number on success.
- By default this uses the production Launchpad instance. Set staging=True to
- use staging.launchpad.net (for testing).
+ By default this uses the production Launchpad hostname. Set
+ hostname to 'launchpad.dev' or 'staging.launchpad.net' to use another
+ instance for testing.
'''
+ #XXX 2010-08-05 matsubara bug=315358
+ # Once bug 315358 is fixed, this function can be converted to use the API
+ # rather than use screenscraping.
ticket = None
global _https_upload_callback
_https_upload_callback = progress_callback
opener = urllib2.build_opener(HTTPSProgressHandler, multipartpost_handler.MultipartPostHandler)
- if staging:
- url = 'https://staging.launchpad.net/+storeblob'
- else:
- url = 'https://launchpad.net/+storeblob'
+ url = 'https://%s/+storeblob' % hostname
result = opener.open(url,
{ 'FORM_SUBMIT': '1', 'field.blob': blob })
ticket = result.info().get('X-Launchpad-Blob-Token')
@@ -814,19 +839,17 @@
#
if __name__ == '__main__':
- import unittest, urllib2, cookielib
+ import unittest, urllib2
crashdb = None
segv_report = None
python_report = None
class _T(unittest.TestCase):
- # this assumes that a source package 'coreutils' exists and builds a
- # binary package 'coreutils'
- test_package = 'coreutils'
- test_srcpackage = 'coreutils'
- known_test_id = 302779
- known_test_id2 = 89040
+ # this assumes that a source package 'alsa-utils' exists and builds a
+ # binary package 'alsa-utils'
+ test_package = 'alsa-utils'
+ test_srcpackage = 'alsa-utils'
#
# Generic tests, should work for all CrashDB implementations
@@ -843,7 +866,72 @@
self.ref_report = apport.Report()
self.ref_report.add_os_info()
self.ref_report.add_user_info()
- self.ref_report['SourcePackage'] = 'coreutils'
+ self.ref_report['SourcePackage'] = 'alsa-utils'
+
+ # Objects tests rely on.
+ self.uncommon_description_bug = self._file_uncommon_description_bug()
+ self._create_project('langpack-o-matic')
+
+ # XXX Should create new bug reports, not reuse those.
+ self.known_test_id = self.uncommon_description_bug.id
+ self.known_test_id2 = self._file_uncommon_description_bug().id
+
+ def _create_project(self, name):
+ """Create a project using launchpadlib to be used by tests."""
+ project = self.crashdb.launchpad.projects[name]
+ if not project:
+ self.crashdb.launchpad.projects.new_project(
+ description=name + 'description',
+ display_name=name,
+ name=name,
+ summary=name + 'summary',
+ title=name + 'title')
+
+ def _file_uncommon_description_bug(self):
+ """File a bug report with an uncommon description.
+
+ Example taken from real LP bug 269539. It contains only
+ ProblemType/Architecture/DistroRelease in the description.
+ """
+ title = (
+ u'Three-way merge issues with menu.lst file during kernel'
+ 'updates: "Conflicts found! Please edit'
+ '`/var/run/grub/menu.lst\' and sort them out manually."')
+ uncommon_description = (
+ u'Ubuntu 8.10 failed to install 2.6.27.3 kernal during'
+ 'update\n\nProblemType: Package\nArchitecture:'
+ 'amd64\nDistroRelease: Ubuntu 8.10\n\nDuring a kernel upgrade,'
+ 'under certain conditions, if the user select "Do a 3-way'
+ 'merge" for menu.lst then the following error'
+ 'occurs:\n\nupdate-initramfs: Generating'
+ '/boot/initrd.img-2.6.27-3-generic\nRunning postinst hook'
+ 'script /sbin/update-grub.\nSearching for GRUB installation'
+ 'directory ... found: /boot/grub\nSearching for default file'
+ '... found: /boot/grub/default\nTesting for an existing GRUB'
+ 'menu.lst file ... found: /boot/grub/menu.lst\nSearching for'
+ 'splash image ... none found, skipping ...\nFound kernel:'
+ '/boot/vmlinuz-2.6.27-3-generic\nFound kernel:'
+ '/boot/last-good-boot/vmlinuz\nFound kernel:'
+ '/boot/vmlinuz-2.6.27-2-generic\nFound kernel:'
+ '/boot/vmlinuz-2.6.26-5-generic\nFound kernel:'
+ '/boot/memtest86+.bin\nMerging changes into the new version\n\n'
+ 'Conflicts found! Please edit `/var/run/grub/menu.lst\' and'
+ 'sort them out manually.\n The file'
+ '`/var/run/grub/menu.lst.ucf-new\' has a record of the failed'
+ 'merge of the configuration file.\n\nUser postinst hook script'
+ '[/sbin/update-grub] exited with value 3\ndpkg: error'
+ 'processing linux-image-2.6.27-3-generic (--configure):\n'
+ 'subprocess post-installation script returned error exit status'
+ '3\n\n\n')
+ bug_target = self.crashdb.lp_distro
+ return self.crashdb.launchpad.bugs.createBug(
+ title=title, description=uncommon_description,
+ target=bug_target)
+
+ @property
+ def hostname(self):
+ """Get the hostname for the given crashdb."""
+ return self.crashdb.get_hostname()
def _file_segv_report(self):
'''File a SEGV crash report.
@@ -863,10 +951,10 @@
handle = self.crashdb.upload(r)
self.assert_(handle)
- url = self.crashdb.get_comment_url(r, handle)
- self.assert_(url)
+ bug_target = self.crashdb.get_target(r)
+ self.assert_(bug_target)
- id = self._fill_bug_form(url)
+ id = self._file_bug(bug_target, r, handle)
self.assert_(id > 0)
return id
@@ -878,7 +966,7 @@
global segv_report
id = self._file_segv_report()
segv_report = id
- print >> sys.stderr, '(https://staging.launchpad.net/bugs/%i) ' % id,
+ print >> sys.stderr, '(https://%s/bugs/%i) ' % (self.hostname, id),
def test_1_report_python(self):
'''upload() and get_comment_url() for Python crash
@@ -899,14 +987,14 @@
handle = self.crashdb.upload(r)
self.assert_(handle)
- url = self.crashdb.get_comment_url(r, handle)
- self.assert_(url)
+ bug_target = self.crashdb.get_target(r)
+ self.assert_(bug_target)
- id = self._fill_bug_form(url)
+ id = self._file_bug(bug_target, r, handle)
self.assert_(id > 0)
global python_report
python_report = id
- print >> sys.stderr, '(https://staging.launchpad.net/bugs/%i) ' % id,
+ print >> sys.stderr, '(https://%s/bugs/%i) ' % (self.hostname, id),
def test_2_download(self):
'''download()'''
@@ -994,12 +1082,14 @@
def test_update_description(self):
'''update() with changing description'''
-
- id = self._fill_bug_form(
- 'https://bugs.staging.launchpad.net/%s/+source/bash/+filebug?'
- 'field.title=testbug&field.actions.search=' % self.crashdb.distro)
+ bug_target = self.crashdb.lp_distro.getSourcePackage(name='pmount')
+ bug = self.crashdb.launchpad.bugs.createBug(
+ description='test description for test bug.',
+ target=bug_target,
+ title='testbug')
+ id = bug.id
self.assert_(id > 0)
- print >> sys.stderr, '(https://staging.launchpad.net/bugs/%i) ' % id,
+ print >> sys.stderr, '(https://%s/bugs/%i) ' % (self.hostname, id),
r = apport.Report('Bug')
@@ -1024,14 +1114,16 @@
def test_update_comment(self):
'''update() with appending comment'''
+ bug_target = self.crashdb.lp_distro.getSourcePackage(name='pmount')
# we need to fake an apport description separator here, since we
# want to be lazy and use download() for checking the result
- id = self._fill_bug_form(
- 'https://bugs.staging.launchpad.net/%s/+source/bash/+filebug?'
- 'field.title=testbug&field.actions.search=' %
- self.crashdb.distro, comment='Pr0blem\n\n--- \nProblemType: Bug')
+ bug = self.crashdb.launchpad.bugs.createBug(
+ description='Pr0blem\n\n--- \nProblemType: Bug',
+ target=bug_target,
+ title='testbug')
+ id = bug.id
self.assert_(id > 0)
- print >> sys.stderr, '(https://staging.launchpad.net/bugs/%i) ' % id,
+ print >> sys.stderr, '(https://%s/bugs/%i) ' % (self.hostname, id),
r = apport.Report('Bug')
@@ -1057,11 +1149,14 @@
def test_update_filter(self):
'''update() with a key filter'''
- id = self._fill_bug_form(
- 'https://bugs.staging.launchpad.net/%s/+source/bash/+filebug?'
- 'field.title=testbug&field.actions.search=' % self.crashdb.distro)
+ bug_target = self.crashdb.lp_distro.getSourcePackage(name='pmount')
+ bug = self.crashdb.launchpad.bugs.createBug(
+ description='test description for test bug',
+ target=bug_target,
+ title='testbug')
+ id = bug.id
self.assert_(id > 0)
- print >> sys.stderr, '(https://staging.launchpad.net/bugs/%i) ' % id,
+ print >> sys.stderr, '(https://%s/bugs/%i) ' % (self.hostname, id),
r = apport.Report('Bug')
@@ -1211,7 +1306,7 @@
invalidated by marking it as a duplicate.
'''
id = self._file_segv_report()
- print >> sys.stderr, '(https://staging.launchpad.net/bugs/%i) ' % id,
+ print >> sys.stderr, '(https://%s/bugs/%i) ' % (self.hostname, id),
r = self.crashdb.download(id)
@@ -1246,120 +1341,85 @@
@classmethod
def _get_instance(klass):
'''Create a CrashDB instance'''
-
- return CrashDatabase(os.environ.get('LP_CREDENTIALS'), '',
- {'distro': 'ubuntu', 'staging': True})
-
- def _fill_bug_form(self, url, comment=None):
- '''Fill form for a distro bug and commit the bug.
-
- Return the report ID.
- '''
- cj = cookielib.MozillaCookieJar()
- cj.load(os.path.expanduser('~/.lpcookie.txt'))
- opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
- opener.addheaders = [('Referer', url)]
-
- re_pkg = re.compile('\+source/([\w]+)/')
- re_title = re.compile('<input.*id="field.title".*value="([^"]+)"')
- re_tags = re.compile('<input.*id="field.tags".*value="([^"]*)"')
-
- # parse default field values from reporting page
- while True:
- res = opener.open(url)
- try:
- self.assertEqual(res.getcode(), 200)
- except AttributeError:
- pass # getcode() is new in Python 2.6
- content = res.read()
-
- if 'Please wait while bug data is processed' in content:
- print '.',
- sys.stdout.flush()
- time.sleep(5)
- continue
-
- break
-
- m_pkg = re_pkg.search(url)
- m_title = re_title.search(content)
- m_tags = re_tags.search(content)
-
- # strip off GET arguments from URL
- url = url.split('?')[0]
-
- # create request to file bug
- args = {
- 'packagename_option': 'choose',
- 'field.packagename': m_pkg.group(1),
- 'field.title': m_title.group(1),
- 'field.tags': m_tags.group(1),
- 'field.comment': comment or 'ZOMG!',
- 'field.actions.submit_bug': '1',
- }
-
- res = opener.open(url, data=urllib.urlencode(args))
- try:
- self.assertEqual(res.getcode(), 200)
- except AttributeError:
- pass # getcode() is new in Python 2.6
- self.assert_('+source/%s/+bug/' % m_pkg.group(1) in res.geturl())
- id = res.geturl().split('/')[-1]
- return int(id)
-
- def _fill_bug_form_project(self, url):
- '''Fill form for a project bug and commit the bug.
-
- Return the report ID.
- '''
- cj = cookielib.MozillaCookieJar()
- cj.load(os.path.expanduser('~/.lpcookie.txt'))
- opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
- opener.addheaders = [('Referer', url)]
-
- m = re.search('launchpad.net/([^/]+)/\+filebug', url)
- assert m
- project = m.group(1)
-
- re_title = re.compile('<input.*id="field.title".*value="([^"]+)"')
- re_tags = re.compile('<input.*id="field.tags".*value="([^"]+)"')
-
- # parse default field values from reporting page
- while True:
- res = opener.open(url)
- try:
- self.assertEqual(res.getcode(), 200)
- except AttributeError:
- pass # getcode() is new in Python 2.6
- content = res.read()
-
- if 'Please wait while bug data is processed' in content:
- print '.',
- sys.stdout.flush()
- time.sleep(5)
- continue
-
- break
-
- m_title = re_title.search(content)
- m_tags = re_tags.search(content)
-
- # strip off GET arguments from URL
- url = url.split('?')[0]
-
- # create request to file bug
- args = {
- 'field.title': m_title.group(1),
- 'field.tags': m_tags.group(1),
- 'field.comment': 'ZOMG!',
- 'field.actions.submit_bug': '1',
- }
-
- res = opener.open(url, data=urllib.urlencode(args))
- self.assertEqual(res.getcode(), 200)
- self.assert_(('launchpad.net/%s/+bug' % project) in res.geturl())
- id = res.geturl().split('/')[-1]
- return int(id)
+ launchpad_instance = os.environ.get('APPORT_LAUNCHPAD_INSTANCE')
+
+ return CrashDatabase(os.environ.get('LP_CREDENTIALS'), '',
+ {'distro': 'ubuntu',
+ 'launchpad_instance': launchpad_instance})
+
+ def _get_librarian_hostname(self):
+ """Return the librarian hostname according to the LP hostname used."""
+ hostname = self.crashdb.get_hostname()
+ if 'staging' in hostname:
+ return 'staging.launchpadlibrarian.net'
+ else:
+ return 'launchpad.dev:58080'
+
+ def _file_bug(self, bug_target, report, handle, comment=None):
+ """File a bug using launchpadlib."""
+ bug_title = report.get('Title', report.standard_title())
+
+ blob_info = self.crashdb.launchpad.temporary_blobs.fetch(
+ token=handle)
+ # XXX 2010-08-03 matsubara bug=612990:
+ # Can't fetch the blob directly, so let's load it from the
+ # representation.
+ blob = self.crashdb.launchpad.load(blob_info['self_link'])
+ #XXX Need to find a way to trigger the job that process the blob
+ # rather polling like this. This makes the test suite take forever
+ # to run.
+ while not blob.hasBeenProcessed():
+ time.sleep(1)
+
+ # processed_blob contains info about privacy, additional comments
+ # and attachments.
+ processed_blob = blob.getProcessedData()
+
+ bug = self.crashdb.launchpad.bugs.createBug(
+ description=processed_blob['extra_description'],
+ private=processed_blob['private'],
+ tags=processed_blob['initial_tags'],
+ target=bug_target,
+ title=bug_title)
+
+ for comment in processed_blob['comments']:
+ bug.newMessage(content=comment)
+
+ # Ideally, one would be able to retrieve the attachment content
+ # from the ProblemReport object or from the processed_blob.
+ # Unfortunately the processed_blob only give us the Launchpad
+ # librarian file_alias_id, so that's why we need to
+ # download it again and upload to the bug report. It'd be even
+ # better if addAttachment could work like linkAttachment, the LP
+ # api used in the +filebug web UI, but there are security concerns
+ # about the way linkAttachment works.
+ librarian_url = 'http://%s' % self._get_librarian_hostname()
+ for attachment in processed_blob['attachments']:
+ filename = description = attachment['description']
+ # Download the attachment data.
+ data = urllib.urlopen(urllib.basejoin(librarian_url,
+ str(attachment['file_alias_id']) + '/' + filename)).read()
+ # Add the attachment to the newly created bug report.
+ bug.addAttachment(
+ comment=filename,
+ data=data,
+ filename=filename,
+ description=description)
+
+ for subscriber in processed_blob['subscribers']:
+ sub = self.crashdb.launchpad.people[subscriber]
+ if sub:
+ bug.subscribe(person=sub)
+
+ for submission_key in processed_blob['hwdb_submission_keys']:
+ # XXX 2010-08-04 matsubara bug=628889:
+ # Can't fetch the submission directly, so let's load it
+ # from the representation.
+ submission = self.crashdb.launchpad.load(
+ 'https://api.%s/beta/+hwdb/+submission/%s'
+ % (self.crashdb.get_hostname(), submission_key))
+ bug.linkHWSubmission(submission=submission)
+ return int(bug.id)
def _mark_needs_retrace(self, id):
'''Mark a report ID as needing retrace.'''
@@ -1406,10 +1466,12 @@
def test_project(self):
'''reporting crashes against a project instead of a distro'''
+ launchpad_instance = os.environ.get('APPORT_LAUNCHPAD_INSTANCE')
# crash database for langpack-o-matic project (this does not have
# packages in any distro)
- crashdb = CrashDatabase(os.environ.get('LP_CREDENTIALS'), '',
- {'project': 'langpack-o-matic', 'staging': True})
+ crashdb = CrashDatabase(os.environ.get('LP_CREDENTIALS'), '',
+ {'project': 'langpack-o-matic',
+ 'launchpad_instance': launchpad_instance})
self.assertEqual(crashdb.distro, None)
# create Python crash report
@@ -1426,12 +1488,12 @@
# file it
handle = crashdb.upload(r)
self.assert_(handle)
- url = crashdb.get_comment_url(r, handle)
- self.assert_('launchpad.net/langpack-o-matic/+filebug' in url)
+ bug_target = crashdb.get_target(r)
+ self.assertEqual(bug_target.name, 'langpack-o-matic')
- id = self._fill_bug_form_project(url)
+ id = self._file_bug(bug_target, r, handle)
self.assert_(id > 0)
- print >> sys.stderr, '(https://staging.launchpad.net/bugs/%i) ' % id,
+ print >> sys.stderr, '(https://%s/bugs/%i) ' % (self.hostname, id),
# update
r = crashdb.download(id)
@@ -1454,7 +1516,7 @@
'''download() of uncommon description formats'''
# only ProblemType/Architecture/DistroRelease in description
- r = self.crashdb.download(269539)
+ r = self.crashdb.download(self.uncommon_description_bug.id)
self.assertEqual(r['ProblemType'], 'Package')
self.assertEqual(r['Architecture'], 'amd64')
self.assert_(r['DistroRelease'].startswith('Ubuntu '))
@@ -1465,10 +1527,12 @@
assert segv_report, 'you need to run test_1_report_segv() first'
- db = CrashDatabase(os.environ.get('LP_CREDENTIALS'), '',
- {'distro': 'ubuntu', 'staging': True,
- 'escalation_tag': 'omgkittens',
- 'escalation_subscription': 'apport-hackers'})
+ launchpad_instance = os.environ.get('APPORT_LAUNCHPAD_INSTANCE')
+ db = CrashDatabase(os.environ.get('LP_CREDENTIALS'), '',
+ {'distro': 'ubuntu',
+ 'launchpad_instance': launchpad_instance,
+ 'escalation_tag': 'omgkittens',
+ 'escalation_subscription': 'apport-hackers'})
count = 0
p = db.launchpad.people[db.options['escalation_subscription']].self_link
@@ -1481,13 +1545,13 @@
db.close_duplicate(b, segv_report)
b = db.launchpad.bugs[segv_report]
has_escalation_tag = db.options['escalation_tag'] in b.tags
- has_escalation_subsciption = any([s.person_link == p for s in b.subscriptions])
+ has_escalation_subscription = any([s.person_link == p for s in b.subscriptions])
if count <= 10:
self.failIf(has_escalation_tag)
- self.failIf(has_escalation_subsciption)
+ self.failIf(has_escalation_subscription)
else:
self.assert_(has_escalation_tag)
- self.assert_(has_escalation_subsciption)
+ self.assert_(has_escalation_subscription)
finally:
for b in range(first_dup, first_dup+count):
print 'R%i' % b,
@@ -1510,7 +1574,7 @@
t.target = self.crashdb.launchpad.distributions['ubuntu'].getSourcePackage(name='pmount')
t.status = 'Invalid'
t.lp_save()
- b.addTask(target=self.crashdb.launchpad.projects['coreutils'])
+ b.addTask(target=self.crashdb.launchpad.projects['alsa-utils'])
b.addTask(target=self.crashdb.launchpad.distributions['ubuntu'])
self.crashdb._mark_dup_checked(python_report, self.ref_report)
@@ -1522,11 +1586,11 @@
# upstream task should be unmodified
b = self.crashdb.launchpad.bugs[python_report]
- self.assertEqual(b.bug_tasks[0].bug_target_name, 'coreutils')
+ self.assertEqual(b.bug_tasks[0].bug_target_name, 'alsa-utils')
self.assertEqual(b.bug_tasks[0].status, 'New')
# package-less distro task should have package name fixed
- self.assertEqual(b.bug_tasks[1].bug_target_name, 'coreutils (Ubuntu)')
+ self.assertEqual(b.bug_tasks[1].bug_target_name, 'alsa-utils (Ubuntu)')
self.assertEqual(b.bug_tasks[1].status, 'New')
# invalid pmount task should be unmodified
Follow ups