Back to index

apport  2.3
Public Member Functions | Public Attributes | Static Public Attributes | Private Member Functions
apport.crashdb_impl.launchpad._T Class Reference
Collaboration diagram for apport.crashdb_impl.launchpad._T:
Collaboration graph
[legend]

List of all members.

Public Member Functions

def setUp
def hostname
def get_segv_report
def get_python_report
def get_uncommon_description_report
def test_1_download
def test_2_update_traces
def test_get_comment_url
def test_update_description
def test_update_comment
def test_update_filter
def test_get_distro_release
def test_get_affected_packages
def test_is_reporter
def test_can_update
def test_duplicates
def test_marking_segv
def test_marking_project
def test_marking_python
def test_update_traces_invalid
def test_get_fixed_version
def test_project
def test_download_robustness
def test_escalation
def test_marking_python_task_mangle

Public Attributes

 crashdb
 ref_report

Static Public Attributes

string test_package = 'coreutils'
string test_srcpackage = 'coreutils'

Private Member Functions

def _create_project
def _get_instance
def _get_bug_target
def _file_bug
def _mark_needs_retrace
def _mark_needs_dupcheck
def _mark_report_fixed
def _mark_report_new
def _verify_marked_regression
def _generate_sigsegv_report

Detailed Description

Definition at line 1056 of file launchpad.py.


Member Function Documentation

def apport.crashdb_impl.launchpad._T._create_project (   self,
  name 
) [private]
Create a project using launchpadlib to be used by tests.

Definition at line 1082 of file launchpad.py.

01082 
01083         def _create_project(self, name):
01084             '''Create a project using launchpadlib to be used by tests.'''
01085 
01086             project = self.crashdb.launchpad.projects[name]
01087             if not project:
01088                 self.crashdb.launchpad.projects.new_project(
01089                     description=name + 'description',
01090                     display_name=name,
01091                     name=name,
01092                     summary=name + 'summary',
01093                     title=name + 'title')

def apport.crashdb_impl.launchpad._T._file_bug (   self,
  bug_target,
  report,
  description = None 
) [private]
File a bug report for a report.

Return the bug ID.

Definition at line 1662 of file launchpad.py.

01662 
01663         def _file_bug(self, bug_target, report, description=None):
01664             '''File a bug report for a report.
01665 
01666             Return the bug ID.
01667             '''
01668             # unfortunately staging's +storeblob API hardly ever works, so we
01669             # must avoid using it. Fake it by manually doing the comments and
01670             # attachments that +filebug would ordinarily do itself when given a
01671             # blob handle.
01672 
01673             if description is None:
01674                 description = 'some description'
01675 
01676             mime = self.crashdb._generate_upload_blob(report)
01677             msg = email.message_from_file(mime)
01678             mime.close()
01679             msg_iter = msg.walk()
01680 
01681             # first one is the multipart container
01682             header = msg_iter.next()
01683             assert header.is_multipart()
01684 
01685             # second part should be an inline text/plain attachments with all short
01686             # fields
01687             part = msg_iter.next()
01688             assert not part.is_multipart()
01689             assert part.get_content_type() == 'text/plain'
01690             description += '\n\n' + part.get_payload(decode=True).decode('UTF-8', 'replace')
01691 
01692             # create the bug from header and description data
01693             bug = self.crashdb.launchpad.bugs.createBug(
01694                 description=description,
01695                 private=(header['Private'] == 'yes'),
01696                 tags=header['Tags'].split(),
01697                 target=bug_target,
01698                 title=report.get('Title', report.standard_title()))
01699 
01700             # nwo add the attachments
01701             for part in msg_iter:
01702                 assert not part.is_multipart()
01703                 bug.addAttachment(comment='',
01704                                   description=part.get_filename(),
01705                                   content_type=None,
01706                                   data=part.get_payload(decode=True),
01707                                   filename=part.get_filename(), is_patch=False)
01708 
01709             for subscriber in header['Subscribers'].split():
01710                 sub = self.crashdb.launchpad.people[subscriber]
01711                 if sub:
01712                     bug.subscribe(person=sub)
01713 
01714             return bug.id

Here is the caller graph for this function:

def apport.crashdb_impl.launchpad._T._generate_sigsegv_report (   klass,
  signal = '11' 
) [private]
Create a test executable which will die with a SIGSEGV, generate a
core dump for it, create a problem report with those two arguments
(ExecutablePath and CoreDump) and call add_gdb_info().

Return the apport.report.Report.

Definition at line 1886 of file launchpad.py.

01886 
01887         def _generate_sigsegv_report(klass, signal='11'):
01888             '''Create a test executable which will die with a SIGSEGV, generate a
01889             core dump for it, create a problem report with those two arguments
01890             (ExecutablePath and CoreDump) and call add_gdb_info().
01891 
01892             Return the apport.report.Report.
01893             '''
01894             workdir = None
01895             orig_cwd = os.getcwd()
01896             pr = apport.report.Report()
01897             try:
01898                 workdir = tempfile.mkdtemp()
01899                 atexit.register(shutil.rmtree, workdir)
01900                 os.chdir(workdir)
01901 
01902                 # create a test executable
01903                 with open('crash.c', 'w') as fd:
01904                     fd.write('''
01905 int f(x) {
01906     int* p = 0; *p = x;
01907     return x+1;
01908 }
01909 int main() { return f(42); }
01910 ''')
01911                 assert subprocess.call(['gcc', '-g', 'crash.c', '-o', 'crash']) == 0
01912                 assert os.path.exists('crash')
01913 
01914                 # call it through gdb and dump core
01915                 subprocess.call(['gdb', '--batch', '--ex', 'run', '--ex',
01916                                  'generate-core-file core', './crash'], stdout=subprocess.PIPE)
01917                 assert os.path.exists('core')
01918                 subprocess.check_call(['sync'])
01919                 assert subprocess.call(['readelf', '-n', 'core'],
01920                                        stdout=subprocess.PIPE) == 0
01921 
01922                 pr['ExecutablePath'] = os.path.join(workdir, 'crash')
01923                 pr['CoreDump'] = (os.path.join(workdir, 'core'),)
01924                 pr['Signal'] = signal
01925 
01926                 pr.add_gdb_info()
01927             finally:
01928                 os.chdir(orig_cwd)
01929 
01930             return pr
01931 
    unittest.main()

Here is the caller graph for this function:

def apport.crashdb_impl.launchpad._T._get_bug_target (   self,
  db,
  report 
) [private]
Return the bug_target for this report.

Definition at line 1651 of file launchpad.py.

01651 
01652         def _get_bug_target(self, db, report):
01653             '''Return the bug_target for this report.'''
01654 
01655             project = db.options.get('project')
01656             if 'SourcePackage' in report:
01657                 return db.lp_distro.getSourcePackage(name=report['SourcePackage'])
01658             elif project:
01659                 return db.launchpad.projects[project]
01660             else:
01661                 return self.lp_distro

Here is the call graph for this function:

Here is the caller graph for this function:

Create a CrashDB instance

Definition at line 1642 of file launchpad.py.

01642 
01643         def _get_instance(klass):
01644             '''Create a CrashDB instance'''
01645 
01646             launchpad_instance = os.environ.get('APPORT_LAUNCHPAD_INSTANCE') or 'staging'
01647 
01648             return CrashDatabase(os.environ.get('LP_CREDENTIALS'),
01649                                  {'distro': 'ubuntu',
01650                                   'launchpad_instance': launchpad_instance})

Here is the caller graph for this function:

def apport.crashdb_impl.launchpad._T._mark_needs_dupcheck (   self,
  id 
) [private]
Mark a report ID as needing duplicate check.

Definition at line 1723 of file launchpad.py.

01723 
01724         def _mark_needs_dupcheck(self, id):
01725             '''Mark a report ID as needing duplicate check.'''
01726 
01727             bug = self.crashdb.launchpad.bugs[id]
01728             if 'need-duplicate-check' not in bug.tags:
01729                 bug.tags = bug.tags + ['need-duplicate-check']
01730                 bug.lp_save()

Here is the caller graph for this function:

def apport.crashdb_impl.launchpad._T._mark_needs_retrace (   self,
  id 
) [private]
Mark a report ID as needing retrace.

Definition at line 1715 of file launchpad.py.

01715 
01716         def _mark_needs_retrace(self, id):
01717             '''Mark a report ID as needing retrace.'''
01718 
01719             bug = self.crashdb.launchpad.bugs[id]
01720             if self.crashdb.arch_tag not in bug.tags:
01721                 bug.tags = bug.tags + [self.crashdb.arch_tag]
01722                 bug.lp_save()

Here is the caller graph for this function:

def apport.crashdb_impl.launchpad._T._mark_report_fixed (   self,
  id 
) [private]
Close a report ID as "fixed".

Definition at line 1731 of file launchpad.py.

01731 
01732         def _mark_report_fixed(self, id):
01733             '''Close a report ID as "fixed".'''
01734 
01735             bug = self.crashdb.launchpad.bugs[id]
01736             tasks = list(bug.bug_tasks)
01737             assert len(tasks) == 1
01738             t = tasks[0]
01739             t.status = 'Fix Released'
01740             t.lp_save()

Here is the caller graph for this function:

def apport.crashdb_impl.launchpad._T._mark_report_new (   self,
  id 
) [private]
Reopen a report ID as "new".

Definition at line 1741 of file launchpad.py.

01741 
01742         def _mark_report_new(self, id):
01743             '''Reopen a report ID as "new".'''
01744 
01745             bug = self.crashdb.launchpad.bugs[id]
01746             tasks = list(bug.bug_tasks)
01747             assert len(tasks) == 1
01748             t = tasks[0]
01749             t.status = 'New'
01750             t.lp_save()

Here is the caller graph for this function:

Verify that report ID is marked as regression.

Definition at line 1751 of file launchpad.py.

01751 
01752         def _verify_marked_regression(self, id):
01753             '''Verify that report ID is marked as regression.'''
01754 
01755             bug = self.crashdb.launchpad.bugs[id]
01756             self.assertTrue('regression-retracer' in bug.tags)

Here is the caller graph for this function:

Generate Python crash report.

Return the ID.

Definition at line 1135 of file launchpad.py.

01135 
01136         def get_python_report(self):
01137             '''Generate Python crash report.
01138 
01139             Return the ID.
01140             '''
01141             global _python_report
01142             if _python_report is not None:
01143                 return _python_report
01144 
01145             r = apport.Report('Crash')
01146             r['ExecutablePath'] = '/bin/foo'
01147             r['Traceback'] = '''Traceback (most recent call last):
01148   File "/bin/foo", line 67, in fuzz
01149     print(weird)
01150 NameError: global name 'weird' is not defined'''
01151             r['Tags'] = 'boogus pybogus'
01152             r.add_package_info(self.test_package)
01153             r.add_os_info()
01154             r.add_user_info()
01155             self.assertEqual(r.standard_title(),
01156                              "foo crashed with NameError in fuzz(): global name 'weird' is not defined")
01157 
01158             bug_target = self._get_bug_target(self.crashdb, r)
01159             self.assertTrue(bug_target)
01160 
01161             id = self._file_bug(bug_target, r)
01162             self.assertTrue(id > 0)
01163             sys.stderr.write('(Created Python report: https://%s/bugs/%i) ' % (self.hostname, id))
01164             _python_report = id
01165             return id

Here is the call graph for this function:

Here is the caller graph for this function:

def apport.crashdb_impl.launchpad._T.get_segv_report (   self,
  force_fresh = False 
)
Generate SEGV crash report.

This is only done once, subsequent calls will return the already
existing ID, unless force_fresh is True.

Return the ID.

Definition at line 1100 of file launchpad.py.

01100 
01101         def get_segv_report(self, force_fresh=False):
01102             '''Generate SEGV crash report.
01103 
01104             This is only done once, subsequent calls will return the already
01105             existing ID, unless force_fresh is True.
01106 
01107             Return the ID.
01108             '''
01109             global _segv_report
01110             if not force_fresh and _segv_report is not None:
01111                 return _segv_report
01112 
01113             r = self._generate_sigsegv_report()
01114             r.add_package_info(self.test_package)
01115             r.add_os_info()
01116             r.add_gdb_info()
01117             r.add_user_info()
01118             self.assertEqual(r.standard_title(), 'crash crashed with SIGSEGV in f()')
01119 
01120             # add some binary gibberish which isn't UTF-8
01121             r['ShortGibberish'] = ' "]\xb6"\n'
01122             r['LongGibberish'] = 'a\nb\nc\nd\ne\n\xff\xff\xff\n\f'
01123 
01124             # create a bug for the report
01125             bug_target = self._get_bug_target(self.crashdb, r)
01126             self.assertTrue(bug_target)
01127 
01128             id = self._file_bug(bug_target, r)
01129             self.assertTrue(id > 0)
01130 
01131             sys.stderr.write('(Created SEGV report: https://%s/bugs/%i) ' % (self.hostname, id))
01132             if not force_fresh:
01133                 _segv_report = id
01134             return id

Here is the call graph for this function:

Here is the caller graph for this function:

def apport.crashdb_impl.launchpad._T.get_uncommon_description_report (   self,
  force_fresh = False 
)
File a bug report with an uncommon description.

This is only done once, subsequent calls will return the already
existing ID, unless force_fresh is True.

Example taken from real LP bug 269539. It contains only
ProblemType/Architecture/DistroRelease in the description, and has
free-form description text after the Apport data.

Return the ID.

Definition at line 1166 of file launchpad.py.

01166 
01167         def get_uncommon_description_report(self, force_fresh=False):
01168             '''File a bug report with an uncommon description.
01169 
01170             This is only done once, subsequent calls will return the already
01171             existing ID, unless force_fresh is True.
01172 
01173             Example taken from real LP bug 269539. It contains only
01174             ProblemType/Architecture/DistroRelease in the description, and has
01175             free-form description text after the Apport data.
01176 
01177             Return the ID.
01178             '''
01179             global _uncommon_description_report
01180             if not force_fresh and _uncommon_description_report is not None:
01181                 return _uncommon_description_report
01182 
01183             desc = '''problem
01184 
01185 ProblemType: Package
01186 Architecture: amd64
01187 DistroRelease: Ubuntu 8.10
01188 
01189 more text
01190 
01191 and more
01192 '''
01193             bug = self.crashdb.launchpad.bugs.createBug(
01194                 title=b'mixed description bug'.encode(),
01195                 description=desc,
01196                 target=self.crashdb.lp_distro)
01197             sys.stderr.write('(Created uncommon description: https://%s/bugs/%i) ' % (self.hostname, bug.id))
01198 
01199             if not force_fresh:
01200                 _uncommon_description_report = bug.id
01201             return bug.id

Here is the call graph for this function:

Here is the caller graph for this function:

Get the Launchpad hostname for the given crashdb.

Definition at line 1095 of file launchpad.py.

01095 
01096         def hostname(self):
01097             '''Get the Launchpad hostname for the given crashdb.'''
01098 
01099             return self.crashdb.get_hostname()

Here is the caller graph for this function:

Definition at line 1066 of file launchpad.py.

01066 
01067         def setUp(self):
01068             global crashdb
01069             if not crashdb:
01070                 crashdb = self._get_instance()
01071             self.crashdb = crashdb
01072 
01073             # create a local reference report so that we can compare
01074             # DistroRelease, Architecture, etc.
01075             self.ref_report = apport.Report()
01076             self.ref_report.add_os_info()
01077             self.ref_report.add_user_info()
01078             self.ref_report['SourcePackage'] = 'coreutils'
01079 
01080             # Objects tests rely on.
01081             self._create_project('langpack-o-matic')

Here is the call graph for this function:

download()

Definition at line 1202 of file launchpad.py.

01202 
01203         def test_1_download(self):
01204             '''download()'''
01205 
01206             r = self.crashdb.download(self.get_segv_report())
01207             self.assertEqual(r['ProblemType'], 'Crash')
01208             self.assertEqual(r['Title'], 'crash crashed with SIGSEGV in f()')
01209             self.assertEqual(r['DistroRelease'], self.ref_report['DistroRelease'])
01210             self.assertEqual(r['Architecture'], self.ref_report['Architecture'])
01211             self.assertEqual(r['Uname'], self.ref_report['Uname'])
01212             self.assertEqual(r.get('NonfreeKernelModules'),
01213                              self.ref_report.get('NonfreeKernelModules'))
01214             self.assertEqual(r.get('UserGroups'), self.ref_report.get('UserGroups'))
01215             tags = set(r['Tags'].split())
01216             self.assertEqual(tags, set([self.crashdb.arch_tag, 'apport-crash',
01217                                         apport.packaging.get_system_architecture()]))
01218 
01219             self.assertEqual(r['Signal'], '11')
01220             self.assertTrue(r['ExecutablePath'].endswith('/crash'))
01221             self.assertEqual(r['SourcePackage'], self.test_srcpackage)
01222             self.assertTrue(r['Package'].startswith(self.test_package + ' '))
01223             self.assertTrue('f (x=42)' in r['Stacktrace'])
01224             self.assertTrue('f (x=42)' in r['StacktraceTop'])
01225             self.assertTrue('f (x=42)' in r['ThreadStacktrace'])
01226             self.assertTrue(len(r['CoreDump']) > 1000)
01227             self.assertTrue('Dependencies' in r)
01228             self.assertTrue('Disassembly' in r)
01229             self.assertTrue('Registers' in r)
01230 
01231             # check tags
01232             r = self.crashdb.download(self.get_python_report())
01233             tags = set(r['Tags'].split())
01234             self.assertEqual(tags, set(['apport-crash', 'boogus', 'pybogus',
01235                                         'need-duplicate-check', apport.packaging.get_system_architecture()]))

Here is the call graph for this function:

update_traces()

Definition at line 1236 of file launchpad.py.

01236 
01237         def test_2_update_traces(self):
01238             '''update_traces()'''
01239 
01240             r = self.crashdb.download(self.get_segv_report())
01241             self.assertTrue('CoreDump' in r)
01242             self.assertTrue('Dependencies' in r)
01243             self.assertTrue('Disassembly' in r)
01244             self.assertTrue('Registers' in r)
01245             self.assertTrue('Stacktrace' in r)
01246             self.assertTrue('ThreadStacktrace' in r)
01247             self.assertEqual(r['Title'], 'crash crashed with SIGSEGV in f()')
01248 
01249             # updating with a useless stack trace retains core dump
01250             r['StacktraceTop'] = '?? ()'
01251             r['Stacktrace'] = 'long\ntrace'
01252             r['ThreadStacktrace'] = 'thread\neven longer\ntrace'
01253             r['FooBar'] = 'bogus'
01254             self.crashdb.update_traces(self.get_segv_report(), r, 'I can has a better retrace?')
01255             r = self.crashdb.download(self.get_segv_report())
01256             self.assertTrue('CoreDump' in r)
01257             self.assertTrue('Dependencies' in r)
01258             self.assertTrue('Disassembly' in r)
01259             self.assertTrue('Registers' in r)
01260             self.assertTrue('Stacktrace' in r)  # TODO: ascertain that it's the updated one
01261             self.assertTrue('ThreadStacktrace' in r)
01262             self.assertFalse('FooBar' in r)
01263             self.assertEqual(r['Title'], 'crash crashed with SIGSEGV in f()')
01264 
01265             tags = self.crashdb.launchpad.bugs[self.get_segv_report()].tags
01266             self.assertTrue('apport-crash' in tags)
01267             self.assertFalse('apport-collected' in tags)
01268 
01269             # updating with a useful stack trace removes core dump
01270             r['StacktraceTop'] = 'read () from /lib/libc.6.so\nfoo (i=1) from /usr/lib/libfoo.so'
01271             r['Stacktrace'] = 'long\ntrace'
01272             r['ThreadStacktrace'] = 'thread\neven longer\ntrace'
01273             self.crashdb.update_traces(self.get_segv_report(), r, 'good retrace!')
01274             r = self.crashdb.download(self.get_segv_report())
01275             self.assertFalse('CoreDump' in r)
01276             self.assertTrue('Dependencies' in r)
01277             self.assertTrue('Disassembly' in r)
01278             self.assertTrue('Registers' in r)
01279             self.assertTrue('Stacktrace' in r)
01280             self.assertTrue('ThreadStacktrace' in r)
01281             self.assertFalse('FooBar' in r)
01282 
01283             # as previous title had standard form, the top function gets
01284             # updated
01285             self.assertEqual(r['Title'], 'crash crashed with SIGSEGV in read()')
01286 
01287             # respects title amendments
01288             bug = self.crashdb.launchpad.bugs[self.get_segv_report()]
01289             bug.title = 'crash crashed with SIGSEGV in f() on exit'
01290             try:
01291                 bug.lp_save()
01292             except HTTPError:
01293                 pass  # LP#336866 workaround
01294             r['StacktraceTop'] = 'read () from /lib/libc.6.so\nfoo (i=1) from /usr/lib/libfoo.so'
01295             self.crashdb.update_traces(self.get_segv_report(), r, 'good retrace with title amendment')
01296             r = self.crashdb.download(self.get_segv_report())
01297             self.assertEqual(r['Title'], 'crash crashed with SIGSEGV in read() on exit')
01298 
01299             # does not destroy custom titles
01300             bug = self.crashdb.launchpad.bugs[self.get_segv_report()]
01301             bug.title = 'crash is crashy'
01302             try:
01303                 bug.lp_save()
01304             except HTTPError:
01305                 pass  # LP#336866 workaround
01306 
01307             r['StacktraceTop'] = 'read () from /lib/libc.6.so\nfoo (i=1) from /usr/lib/libfoo.so'
01308             self.crashdb.update_traces(self.get_segv_report(), r, 'good retrace with custom title')
01309             r = self.crashdb.download(self.get_segv_report())
01310             self.assertEqual(r['Title'], 'crash is crashy')
01311 
01312             # test various situations which caused crashes
01313             r['Stacktrace'] = ''  # empty file
01314             r['ThreadStacktrace'] = '"]\xb6"\n'  # not interpretable as UTF-8, LP #353805
01315             r['StacktraceSource'] = 'a\nb\nc\nd\ne\n\xff\xff\xff\n\f'
01316             self.crashdb.update_traces(self.get_segv_report(), r, 'tests')

Here is the call graph for this function:

can_update()

Definition at line 1457 of file launchpad.py.

01457 
01458         def test_can_update(self):
01459             '''can_update()'''
01460 
01461             self.assertTrue(self.crashdb.can_update(self.get_segv_report()))
01462             self.assertFalse(self.crashdb.can_update(1))

Here is the call graph for this function:

download() of uncommon description formats

Definition at line 1805 of file launchpad.py.

01805 
01806         def test_download_robustness(self):
01807             '''download() of uncommon description formats'''
01808 
01809             # only ProblemType/Architecture/DistroRelease in description
01810             r = self.crashdb.download(self.get_uncommon_description_report())
01811             self.assertEqual(r['ProblemType'], 'Package')
01812             self.assertEqual(r['Architecture'], 'amd64')
01813             self.assertTrue(r['DistroRelease'].startswith('Ubuntu '))

Here is the call graph for this function:

duplicate handling

Definition at line 1463 of file launchpad.py.

01463 
01464         def test_duplicates(self):
01465             '''duplicate handling'''
01466 
01467             # initially we have no dups
01468             self.assertEqual(self.crashdb.duplicate_of(self.get_segv_report()), None)
01469             self.assertEqual(self.crashdb.get_fixed_version(self.get_segv_report()), None)
01470 
01471             segv_id = self.get_segv_report()
01472             known_test_id = self.get_uncommon_description_report()
01473             known_test_id2 = self.get_uncommon_description_report(force_fresh=True)
01474 
01475             # dupe our segv_report and check that it worked; then undupe it
01476             r = self.crashdb.download(segv_id)
01477             self.crashdb.close_duplicate(r, segv_id, known_test_id)
01478             self.assertEqual(self.crashdb.duplicate_of(segv_id), known_test_id)
01479 
01480             # this should be a no-op
01481             self.crashdb.close_duplicate(r, segv_id, known_test_id)
01482             self.assertEqual(self.crashdb.duplicate_of(segv_id), known_test_id)
01483 
01484             self.assertEqual(self.crashdb.get_fixed_version(segv_id), 'invalid')
01485             self.crashdb.close_duplicate(r, segv_id, None)
01486             self.assertEqual(self.crashdb.duplicate_of(segv_id), None)
01487             self.assertEqual(self.crashdb.get_fixed_version(segv_id), None)
01488 
01489             # this should have removed attachments; note that Stacktrace is
01490             # short, and thus inline
01491             r = self.crashdb.download(self.get_segv_report())
01492             self.assertFalse('CoreDump' in r)
01493             self.assertFalse('Disassembly' in r)
01494             self.assertFalse('ProcMaps' in r)
01495             self.assertFalse('ProcStatus' in r)
01496             self.assertFalse('Registers' in r)
01497             self.assertFalse('ThreadStacktrace' in r)
01498 
01499             # now try duplicating to a duplicate bug; this should automatically
01500             # transition to the master bug
01501             self.crashdb.close_duplicate(apport.Report(), known_test_id,
01502                                          known_test_id2)
01503             self.crashdb.close_duplicate(r, segv_id, known_test_id)
01504             self.assertEqual(self.crashdb.duplicate_of(segv_id),
01505                              known_test_id2)
01506 
01507             self.crashdb.close_duplicate(apport.Report(), known_test_id, None)
01508             self.crashdb.close_duplicate(apport.Report(), known_test_id2, None)
01509             self.crashdb.close_duplicate(r, segv_id, None)
01510 
01511             # this should be a no-op
01512             self.crashdb.close_duplicate(apport.Report(), known_test_id, None)
01513             self.assertEqual(self.crashdb.duplicate_of(known_test_id), None)
01514 
01515             self.crashdb.mark_regression(segv_id, known_test_id)
01516             self._verify_marked_regression(segv_id)

Here is the call graph for this function:

Escalating bugs with more than 10 duplicates

Definition at line 1814 of file launchpad.py.

01814 
01815         def test_escalation(self):
01816             '''Escalating bugs with more than 10 duplicates'''
01817 
01818             launchpad_instance = os.environ.get('APPORT_LAUNCHPAD_INSTANCE') or 'staging'
01819             db = CrashDatabase(os.environ.get('LP_CREDENTIALS'),
01820                                {'distro': 'ubuntu',
01821                                 'launchpad_instance': launchpad_instance,
01822                                 'escalation_tag': 'omgkittens',
01823                                 'escalation_subscription': 'apport-hackers'})
01824 
01825             count = 0
01826             p = db.launchpad.people[db.options['escalation_subscription']].self_link
01827             first_dup = 59
01828             try:
01829                 for b in range(first_dup, first_dup + 13):
01830                     count += 1
01831                     sys.stderr.write('%i ' % b)
01832                     db.close_duplicate(apport.Report(), b, self.get_segv_report())
01833                     b = db.launchpad.bugs[self.get_segv_report()]
01834                     has_escalation_tag = db.options['escalation_tag'] in b.tags
01835                     has_escalation_subscription = any([s.person_link == p for s in b.subscriptions])
01836                     if count <= 10:
01837                         self.assertFalse(has_escalation_tag)
01838                         self.assertFalse(has_escalation_subscription)
01839                     else:
01840                         self.assertTrue(has_escalation_tag)
01841                         self.assertTrue(has_escalation_subscription)
01842             finally:
01843                 for b in range(first_dup, first_dup + count):
01844                     sys.stderr.write('R%i ' % b)
01845                     db.close_duplicate(apport.Report(), b, None)
01846             sys.stderr.write('\n')

Here is the call graph for this function:

get_affected_packages()

Definition at line 1445 of file launchpad.py.

01445 
01446         def test_get_affected_packages(self):
01447             '''get_affected_packages()'''
01448 
01449             self.assertEqual(self.crashdb.get_affected_packages(self.get_segv_report()),
01450                              [self.ref_report['SourcePackage']])

Here is the call graph for this function:

get_comment_url() for non-ASCII titles

Definition at line 1317 of file launchpad.py.

01317 
01318         def test_get_comment_url(self):
01319             '''get_comment_url() for non-ASCII titles'''
01320 
01321             # UTF-8 bytestring, works in both python 2.7 and 3
01322             title = b'1\xc3\xa4\xe2\x99\xa52'
01323 
01324             # distro, UTF-8 bytestring
01325             r = apport.Report('Bug')
01326             r['Title'] = title
01327             url = self.crashdb.get_comment_url(r, 42)
01328             self.assertTrue(url.endswith('/ubuntu/+filebug/42?field.title=1%C3%A4%E2%99%A52'))
01329 
01330             # distro, unicode
01331             r['Title'] = title.decode('UTF-8')
01332             url = self.crashdb.get_comment_url(r, 42)
01333             self.assertTrue(url.endswith('/ubuntu/+filebug/42?field.title=1%C3%A4%E2%99%A52'))
01334 
01335             # package, unicode
01336             r['SourcePackage'] = 'coreutils'
01337             url = self.crashdb.get_comment_url(r, 42)
01338             self.assertTrue(url.endswith('/ubuntu/+source/coreutils/+filebug/42?field.title=1%C3%A4%E2%99%A52'))

get_distro_release()

Definition at line 1439 of file launchpad.py.

01439 
01440         def test_get_distro_release(self):
01441             '''get_distro_release()'''
01442 
01443             self.assertEqual(self.crashdb.get_distro_release(self.get_segv_report()),
01444                              self.ref_report['DistroRelease'])

Here is the call graph for this function:

get_fixed_version() for fixed bugs

Other cases are already checked in test_marking_segv() (invalid
bugs) and test_duplicates (duplicate bugs) for efficiency.

Definition at line 1622 of file launchpad.py.

01622 
01623         def test_get_fixed_version(self, *args):
01624             '''get_fixed_version() for fixed bugs
01625 
01626             Other cases are already checked in test_marking_segv() (invalid
01627             bugs) and test_duplicates (duplicate bugs) for efficiency.
01628             '''
01629             # staging.launchpad.net often does not have Quantal, so mock-patch
01630             # it to a known value
01631             CrashDatabase._get_source_version.return_value = '3.14'
01632             self._mark_report_fixed(self.get_segv_report())
01633             fixed_ver = self.crashdb.get_fixed_version(self.get_segv_report())
01634             self.assertEqual(fixed_ver, '3.14')
01635             self._mark_report_new(self.get_segv_report())
01636             self.assertEqual(self.crashdb.get_fixed_version(self.get_segv_report()), None)

Here is the call graph for this function:

is_reporter()

Definition at line 1451 of file launchpad.py.

01451 
01452         def test_is_reporter(self):
01453             '''is_reporter()'''
01454 
01455             self.assertTrue(self.crashdb.is_reporter(self.get_segv_report()))
01456             self.assertFalse(self.crashdb.is_reporter(1))

Here is the call graph for this function:

processing status markings for a project CrashDB

Definition at line 1552 of file launchpad.py.

01552 
01553         def test_marking_project(self):
01554             '''processing status markings for a project CrashDB'''
01555 
01556             # create a distro bug
01557             distro_bug = self.crashdb.launchpad.bugs.createBug(
01558                 description='foo',
01559                 tags=self.crashdb.arch_tag,
01560                 target=self.crashdb.lp_distro,
01561                 title='ubuntu distro retrace bug')
01562             #print('distro bug: https://staging.launchpad.net/bugs/%i' % distro_bug.id)
01563 
01564             # create a project crash DB and a bug
01565             launchpad_instance = os.environ.get('APPORT_LAUNCHPAD_INSTANCE') or 'staging'
01566 
01567             project_db = CrashDatabase(
01568                 os.environ.get('LP_CREDENTIALS'),
01569                 {'project': 'langpack-o-matic', 'launchpad_instance': launchpad_instance})
01570             project_bug = project_db.launchpad.bugs.createBug(
01571                 description='bar',
01572                 tags=project_db.arch_tag,
01573                 target=project_db.lp_distro,
01574                 title='project retrace bug')
01575             #print('project bug: https://staging.launchpad.net/bugs/%i' % project_bug.id)
01576 
01577             # on project_db, we recognize the project bug and can mark it
01578             unretraced_before = project_db.get_unretraced()
01579             self.assertTrue(project_bug.id in unretraced_before)
01580             self.assertFalse(distro_bug.id in unretraced_before)
01581             project_db.mark_retraced(project_bug.id)
01582             unretraced_after = project_db.get_unretraced()
01583             self.assertFalse(project_bug.id in unretraced_after)
01584             self.assertEqual(unretraced_before,
01585                              unretraced_after.union(set([project_bug.id])))
01586             self.assertEqual(self.crashdb.get_fixed_version(project_bug.id), None)

processing status markings for interpreter crashes

Definition at line 1587 of file launchpad.py.

01587 
01588         def test_marking_python(self):
01589             '''processing status markings for interpreter crashes'''
01590 
01591             unchecked_before = self.crashdb.get_dup_unchecked()
01592             self.assertTrue(self.get_python_report() in unchecked_before)
01593             self.assertFalse(self.get_segv_report() in unchecked_before)
01594             self.crashdb._mark_dup_checked(self.get_python_report(), self.ref_report)
01595             unchecked_after = self.crashdb.get_dup_unchecked()
01596             self.assertFalse(self.get_python_report() in unchecked_after)
01597             self.assertEqual(unchecked_before,
01598                              unchecked_after.union(set([self.get_python_report()])))
01599             self.assertEqual(self.crashdb.get_fixed_version(self.get_python_report()), None)

Here is the call graph for this function:

source package task fixup for marking interpreter crashes

Definition at line 1847 of file launchpad.py.

01847 
01848         def test_marking_python_task_mangle(self):
01849             '''source package task fixup for marking interpreter crashes'''
01850 
01851             self._mark_needs_dupcheck(self.get_python_report())
01852             unchecked_before = self.crashdb.get_dup_unchecked()
01853             self.assertTrue(self.get_python_report() in unchecked_before)
01854 
01855             # add an upstream task, and remove the package name from the
01856             # package task; _mark_dup_checked is supposed to restore the
01857             # package name
01858             b = self.crashdb.launchpad.bugs[self.get_python_report()]
01859             if b.private:
01860                 b.private = False
01861                 b.lp_save()
01862             t = b.bug_tasks[0]
01863             t.target = self.crashdb.launchpad.distributions['ubuntu']
01864             t.lp_save()
01865             b.addTask(target=self.crashdb.launchpad.projects['coreutils'])
01866 
01867             self.crashdb._mark_dup_checked(self.get_python_report(), self.ref_report)
01868 
01869             unchecked_after = self.crashdb.get_dup_unchecked()
01870             self.assertFalse(self.get_python_report() in unchecked_after)
01871             self.assertEqual(unchecked_before,
01872                              unchecked_after.union(set([self.get_python_report()])))
01873 
01874             # upstream task should be unmodified
01875             b = self.crashdb.launchpad.bugs[self.get_python_report()]
01876             self.assertEqual(b.bug_tasks[0].bug_target_name, 'coreutils')
01877             self.assertEqual(b.bug_tasks[0].status, 'New')
01878 
01879             # package-less distro task should have package name fixed
01880             self.assertEqual(b.bug_tasks[1].bug_target_name, 'coreutils (Ubuntu)')
01881             self.assertEqual(b.bug_tasks[1].status, 'New')
01882 
01883             # should not confuse get_fixed_version()
01884             self.assertEqual(self.crashdb.get_fixed_version(self.get_python_report()), None)

Here is the call graph for this function:

processing status markings for signal crashes

Definition at line 1517 of file launchpad.py.

01517 
01518         def test_marking_segv(self):
01519             '''processing status markings for signal crashes'''
01520 
01521             # mark_retraced()
01522             unretraced_before = self.crashdb.get_unretraced()
01523             self.assertTrue(self.get_segv_report() in unretraced_before)
01524             self.assertFalse(self.get_python_report() in unretraced_before)
01525             self.crashdb.mark_retraced(self.get_segv_report())
01526             unretraced_after = self.crashdb.get_unretraced()
01527             self.assertFalse(self.get_segv_report() in unretraced_after)
01528             self.assertEqual(unretraced_before,
01529                              unretraced_after.union(set([self.get_segv_report()])))
01530             self.assertEqual(self.crashdb.get_fixed_version(self.get_segv_report()), None)
01531 
01532             # mark_retrace_failed()
01533             self._mark_needs_retrace(self.get_segv_report())
01534             self.crashdb.mark_retraced(self.get_segv_report())
01535             self.crashdb.mark_retrace_failed(self.get_segv_report())
01536             unretraced_after = self.crashdb.get_unretraced()
01537             self.assertFalse(self.get_segv_report() in unretraced_after)
01538             self.assertEqual(unretraced_before,
01539                              unretraced_after.union(set([self.get_segv_report()])))
01540             self.assertEqual(self.crashdb.get_fixed_version(self.get_segv_report()), None)
01541 
01542             # mark_retrace_failed() of invalid bug
01543             self._mark_needs_retrace(self.get_segv_report())
01544             self.crashdb.mark_retraced(self.get_segv_report())
01545             self.crashdb.mark_retrace_failed(self.get_segv_report(), "I don't like you")
01546             unretraced_after = self.crashdb.get_unretraced()
01547             self.assertFalse(self.get_segv_report() in unretraced_after)
01548             self.assertEqual(unretraced_before,
01549                              unretraced_after.union(set([self.get_segv_report()])))
01550             self.assertEqual(self.crashdb.get_fixed_version(self.get_segv_report()),
01551                              'invalid')

Here is the call graph for this function:

reporting crashes against a project instead of a distro

Definition at line 1757 of file launchpad.py.

01757 
01758         def test_project(self):
01759             '''reporting crashes against a project instead of a distro'''
01760 
01761             launchpad_instance = os.environ.get('APPORT_LAUNCHPAD_INSTANCE') or 'staging'
01762             # crash database for langpack-o-matic project (this does not have
01763             # packages in any distro)
01764             crashdb = CrashDatabase(os.environ.get('LP_CREDENTIALS'),
01765                                     {'project': 'langpack-o-matic',
01766                                      'launchpad_instance': launchpad_instance})
01767             self.assertEqual(crashdb.distro, None)
01768 
01769             # create Python crash report
01770             r = apport.Report('Crash')
01771             r['ExecutablePath'] = '/bin/foo'
01772             r['Traceback'] = '''Traceback (most recent call last):
01773   File "/bin/foo", line 67, in fuzz
01774     print(weird)
01775 NameError: global name 'weird' is not defined'''
01776             r.add_os_info()
01777             r.add_user_info()
01778             self.assertEqual(r.standard_title(),
01779                              "foo crashed with NameError in fuzz(): global name 'weird' is not defined")
01780 
01781             # file it
01782             bug_target = self._get_bug_target(crashdb, r)
01783             self.assertEqual(bug_target.name, 'langpack-o-matic')
01784 
01785             id = self._file_bug(bug_target, r)
01786             self.assertTrue(id > 0)
01787             sys.stderr.write('(https://%s/bugs/%i) ' % (self.hostname, id))
01788 
01789             # update
01790             r = crashdb.download(id)
01791             r['StacktraceTop'] = 'read () from /lib/libc.6.so\nfoo (i=1) from /usr/lib/libfoo.so'
01792             r['Stacktrace'] = 'long\ntrace'
01793             r['ThreadStacktrace'] = 'thread\neven longer\ntrace'
01794             crashdb.update_traces(id, r, 'good retrace!')
01795             r = crashdb.download(id)
01796 
01797             # test fixed version
01798             self.assertEqual(crashdb.get_fixed_version(id), None)
01799             crashdb.close_duplicate(r, id, self.get_uncommon_description_report())
01800             self.assertEqual(crashdb.duplicate_of(id), self.get_uncommon_description_report())
01801             self.assertEqual(crashdb.get_fixed_version(id), 'invalid')
01802             crashdb.close_duplicate(r, id, None)
01803             self.assertEqual(crashdb.duplicate_of(id), None)
01804             self.assertEqual(crashdb.get_fixed_version(id), None)

Here is the call graph for this function:

update() with appending comment

Definition at line 1371 of file launchpad.py.

01371 
01372         def test_update_comment(self):
01373             '''update() with appending comment'''
01374 
01375             bug_target = self.crashdb.lp_distro.getSourcePackage(name='bash')
01376             # we need to fake an apport description separator here, since we
01377             # want to be lazy and use download() for checking the result
01378             bug = self.crashdb.launchpad.bugs.createBug(
01379                 description='Pr0blem\n\n--- \nProblemType: Bug',
01380                 target=bug_target,
01381                 title='testbug')
01382             id = bug.id
01383             self.assertTrue(id > 0)
01384             sys.stderr.write('(https://%s/bugs/%i) ' % (self.hostname, id))
01385 
01386             r = apport.Report('Bug')
01387 
01388             r['OneLiner'] = 'bogus→'
01389             r['StacktraceTop'] = 'f()\ng()\nh(1)'
01390             r['ShortGoo'] = 'lineone\nlinetwo'
01391             r['DpkgTerminalLog'] = 'one\ntwo\nthree\nfour\nfive\nsix'
01392             r['VarLogDistupgradeBinGoo'] = '\x01' * 1024
01393 
01394             self.crashdb.update(id, r, 'meow', change_description=False)
01395 
01396             r = self.crashdb.download(id)
01397 
01398             self.assertFalse('OneLiner' in r)
01399             self.assertFalse('ShortGoo' in r)
01400             self.assertEqual(r['ProblemType'], 'Bug')
01401             self.assertEqual(r['DpkgTerminalLog'], 'one\ntwo\nthree\nfour\nfive\nsix')
01402             self.assertEqual(r['VarLogDistupgradeBinGoo'], '\x01' * 1024)
01403 
01404             self.assertEqual(self.crashdb.launchpad.bugs[id].tags,
01405                              ['apport-collected'])

Here is the call graph for this function:

update() with changing description

Definition at line 1339 of file launchpad.py.

01339 
01340         def test_update_description(self):
01341             '''update() with changing description'''
01342 
01343             bug_target = self.crashdb.lp_distro.getSourcePackage(name='bash')
01344             bug = self.crashdb.launchpad.bugs.createBug(
01345                 description='test description for test bug.',
01346                 target=bug_target,
01347                 title='testbug')
01348             id = bug.id
01349             self.assertTrue(id > 0)
01350             sys.stderr.write('(https://%s/bugs/%i) ' % (self.hostname, id))
01351 
01352             r = apport.Report('Bug')
01353 
01354             r['OneLiner'] = b'bogus\xe2\x86\x92'.decode('UTF-8')
01355             r['StacktraceTop'] = 'f()\ng()\nh(1)'
01356             r['ShortGoo'] = 'lineone\nlinetwo'
01357             r['DpkgTerminalLog'] = 'one\ntwo\nthree\nfour\nfive\nsix'
01358             r['VarLogDistupgradeBinGoo'] = b'\x01' * 1024
01359 
01360             self.crashdb.update(id, r, 'NotMe', change_description=True)
01361 
01362             r = self.crashdb.download(id)
01363 
01364             self.assertEqual(r['OneLiner'], b'bogus\xe2\x86\x92'.decode('UTF-8'))
01365             self.assertEqual(r['ShortGoo'], 'lineone\nlinetwo')
01366             self.assertEqual(r['DpkgTerminalLog'], 'one\ntwo\nthree\nfour\nfive\nsix')
01367             self.assertEqual(r['VarLogDistupgradeBinGoo'], b'\x01' * 1024)
01368 
01369             self.assertEqual(self.crashdb.launchpad.bugs[id].tags,
01370                              ['apport-collected'])

Here is the call graph for this function:

update() with a key filter

Definition at line 1406 of file launchpad.py.

01406 
01407         def test_update_filter(self):
01408             '''update() with a key filter'''
01409 
01410             bug_target = self.crashdb.lp_distro.getSourcePackage(name='bash')
01411             bug = self.crashdb.launchpad.bugs.createBug(
01412                 description='test description for test bug',
01413                 target=bug_target,
01414                 title='testbug')
01415             id = bug.id
01416             self.assertTrue(id > 0)
01417             sys.stderr.write('(https://%s/bugs/%i) ' % (self.hostname, id))
01418 
01419             r = apport.Report('Bug')
01420 
01421             r['OneLiner'] = 'bogus→'
01422             r['StacktraceTop'] = 'f()\ng()\nh(1)'
01423             r['ShortGoo'] = 'lineone\nlinetwo'
01424             r['DpkgTerminalLog'] = 'one\ntwo\nthree\nfour\nfive\nsix'
01425             r['VarLogDistupgradeBinGoo'] = '\x01' * 1024
01426 
01427             self.crashdb.update(id, r, 'NotMe', change_description=True,
01428                                 key_filter=['ProblemType', 'ShortGoo', 'DpkgTerminalLog'])
01429 
01430             r = self.crashdb.download(id)
01431 
01432             self.assertFalse('OneLiner' in r)
01433             self.assertEqual(r['ShortGoo'], 'lineone\nlinetwo')
01434             self.assertEqual(r['ProblemType'], 'Bug')
01435             self.assertEqual(r['DpkgTerminalLog'], 'one\ntwo\nthree\nfour\nfive\nsix')
01436             self.assertFalse('VarLogDistupgradeBinGoo' in r)
01437 
01438             self.assertEqual(self.crashdb.launchpad.bugs[id].tags, [])

Here is the call graph for this function:

updating an invalid crash

This simulates a race condition where a crash being processed gets
invalidated by marking it as a duplicate.

Definition at line 1600 of file launchpad.py.

01600 
01601         def test_update_traces_invalid(self):
01602             '''updating an invalid crash
01603 
01604             This simulates a race condition where a crash being processed gets
01605             invalidated by marking it as a duplicate.
01606             '''
01607             id = self.get_segv_report(force_fresh=True)
01608 
01609             r = self.crashdb.download(id)
01610 
01611             self.crashdb.close_duplicate(r, id, self.get_segv_report())
01612 
01613             # updating with a useful stack trace removes core dump
01614             r['StacktraceTop'] = 'read () from /lib/libc.6.so\nfoo (i=1) from /usr/lib/libfoo.so'
01615             r['Stacktrace'] = 'long\ntrace'
01616             r['ThreadStacktrace'] = 'thread\neven longer\ntrace'
01617             self.crashdb.update_traces(id, r, 'good retrace!')
01618 
01619             r = self.crashdb.download(id)
01620             self.assertFalse('CoreDump' in r)

Here is the call graph for this function:


Member Data Documentation

Definition at line 1070 of file launchpad.py.

Definition at line 1074 of file launchpad.py.

string apport.crashdb_impl.launchpad._T.test_package = 'coreutils' [static]

Definition at line 1059 of file launchpad.py.

string apport.crashdb_impl.launchpad._T.test_srcpackage = 'coreutils' [static]

Definition at line 1060 of file launchpad.py.


The documentation for this class was generated from the following file: