Back to index

apport  2.4
test_ui.py
Go to the documentation of this file.
00001 # coding: UTF-8
00002 import unittest, shutil, signal, tempfile, resource, pwd, time, os, sys
00003 import subprocess, errno, glob
00004 
00005 try:
00006     from cStringIO import StringIO
00007     StringIO  # pyflakes
00008 except ImportError:
00009     from io import StringIO
00010 from io import BytesIO
00011 
00012 import apport.ui
00013 from apport.ui import _
00014 import apport.report
00015 import problem_report
00016 import apport.crashdb_impl.memory
00017 import stat
00018 
00019 
00020 class TestSuiteUserInterface(apport.ui.UserInterface):
00021     '''Concrete apport.ui.UserInterface suitable for automatic testing'''
00022 
00023     def __init__(self):
00024         # use our dummy crashdb
00025         self.crashdb_conf = tempfile.NamedTemporaryFile()
00026         self.crashdb_conf.write(b'''default = 'testsuite'
00027 databases = {
00028     'testsuite': {
00029         'impl': 'memory',
00030         'bug_pattern_url': None,
00031     }
00032 }
00033 ''')
00034         self.crashdb_conf.flush()
00035 
00036         os.environ['APPORT_CRASHDB_CONF'] = self.crashdb_conf.name
00037 
00038         apport.ui.UserInterface.__init__(self)
00039 
00040         self.crashdb = apport.crashdb_impl.memory.CrashDatabase(
00041             None, {'dummy_data': 1, 'dupdb_url': ''})
00042 
00043         # state of progress dialogs
00044         self.ic_progress_active = False
00045         self.ic_progress_pulses = 0  # count the pulses
00046         self.upload_progress_active = False
00047         self.upload_progress_pulses = 0
00048 
00049         # these store the choices the ui_present_* calls do
00050         self.present_package_error_response = None
00051         self.present_kernel_error_response = None
00052         self.present_details_response = None
00053         self.question_yesno_response = None
00054         self.question_choice_response = None
00055         self.question_file_response = None
00056 
00057         self.opened_url = None
00058         self.present_details_shown = False
00059 
00060         self.clear_msg()
00061 
00062     def clear_msg(self):
00063         # last message box
00064         self.msg_title = None
00065         self.msg_text = None
00066         self.msg_severity = None  # 'warning' or 'error'
00067         self.msg_choices = None
00068 
00069     def ui_present_report_details(self, is_update):
00070         self.present_details_shown = True
00071         return self.present_details_response
00072 
00073     def ui_info_message(self, title, text):
00074         self.msg_title = title
00075         self.msg_text = text
00076         self.msg_severity = 'info'
00077 
00078     def ui_error_message(self, title, text):
00079         self.msg_title = title
00080         self.msg_text = text
00081         self.msg_severity = 'error'
00082 
00083     def ui_start_info_collection_progress(self):
00084         self.ic_progress_pulses = 0
00085         self.ic_progress_active = True
00086 
00087     def ui_pulse_info_collection_progress(self):
00088         assert self.ic_progress_active
00089         self.ic_progress_pulses += 1
00090 
00091     def ui_stop_info_collection_progress(self):
00092         self.ic_progress_active = False
00093 
00094     def ui_start_upload_progress(self):
00095         self.upload_progress_pulses = 0
00096         self.upload_progress_active = True
00097 
00098     def ui_set_upload_progress(self, progress):
00099         assert self.upload_progress_active
00100         self.upload_progress_pulses += 1
00101 
00102     def ui_stop_upload_progress(self):
00103         self.upload_progress_active = False
00104 
00105     def open_url(self, url):
00106         self.opened_url = url
00107 
00108     def ui_question_yesno(self, text):
00109         self.msg_text = text
00110         return self.question_yesno_response
00111 
00112     def ui_question_choice(self, text, options, multiple):
00113         self.msg_text = text
00114         self.msg_choices = options
00115         return self.question_choice_response
00116 
00117     def ui_question_file(self, text):
00118         self.msg_text = text
00119         return self.question_file_response
00120 
00121 
00122 class T(unittest.TestCase):
00123     def setUp(self):
00124         # we test a few strings, don't get confused by translations
00125         for v in ['LANG', 'LANGUAGE', 'LC_MESSAGES', 'LC_ALL']:
00126             try:
00127                 del os.environ[v]
00128             except KeyError:
00129                 pass
00130 
00131         self.orig_report_dir = apport.fileutils.report_dir
00132         apport.fileutils.report_dir = tempfile.mkdtemp()
00133         self.orig_symptom_script_dir = apport.ui.symptom_script_dir
00134         apport.ui.symptom_script_dir = tempfile.mkdtemp()
00135         self.orig_ignore_file = apport.report._ignore_file
00136         (fd, apport.report._ignore_file) = tempfile.mkstemp()
00137         os.close(fd)
00138 
00139         # need to do this to not break ui's ctor
00140         self.orig_argv = sys.argv
00141         sys.argv = ['ui-test']
00142         self.ui = TestSuiteUserInterface()
00143 
00144         # demo report
00145         self.report = apport.Report()
00146         self.report['ExecutablePath'] = '/bin/bash'
00147         self.report['Package'] = 'libfoo1 1-1'
00148         self.report['SourcePackage'] = 'foo'
00149         self.report['Foo'] = 'A' * 1000
00150         self.report['CoreDump'] = problem_report.CompressedValue(b'\x01' * 100000)
00151 
00152         # write demo report into temporary file
00153         self.report_file = tempfile.NamedTemporaryFile()
00154         self.update_report_file()
00155 
00156         # set up our local hook directory
00157         self.hookdir = tempfile.mkdtemp()
00158         self.orig_hook_dir = apport.report._hook_dir
00159         apport.report._hook_dir = self.hookdir
00160 
00161         # test suite should not stumble over local packages
00162         os.environ['APPORT_IGNORE_OBSOLETE_PACKAGES'] = '1'
00163         os.environ['APPORT_DISABLE_DISTRO_CHECK'] = '1'
00164 
00165     def update_report_file(self):
00166         self.report_file.seek(0)
00167         self.report_file.truncate()
00168         self.report.write(self.report_file)
00169         self.report_file.flush()
00170 
00171     def tearDown(self):
00172         sys.argv = self.orig_argv
00173         shutil.rmtree(apport.fileutils.report_dir)
00174         apport.fileutils.report_dir = self.orig_report_dir
00175         self.orig_report_dir = None
00176         shutil.rmtree(apport.ui.symptom_script_dir)
00177         apport.ui.symptom_script_dir = self.orig_symptom_script_dir
00178         self.orig_symptom_script_dir = None
00179 
00180         os.unlink(apport.report._ignore_file)
00181         apport.report._ignore_file = self.orig_ignore_file
00182 
00183         self.ui = None
00184         self.report_file.close()
00185 
00186         self.assertEqual(subprocess.call(['pidof', '/usr/bin/yes']), 1, 'no stray test processes')
00187 
00188         # clean up apport report from _gen_test_crash()
00189         for f in glob.glob('/var/crash/_usr_bin_yes.*.crash'):
00190             try:
00191                 os.unlink(f)
00192             except OSError:
00193                 pass
00194 
00195         shutil.rmtree(self.hookdir)
00196         apport.report._hook_dir = self.orig_hook_dir
00197 
00198     def test_format_filesize(self):
00199         '''format_filesize()'''
00200 
00201         self.assertEqual(self.ui.format_filesize(0), '0.0 KB')
00202         self.assertEqual(self.ui.format_filesize(2048), '2.0 KB')
00203         self.assertEqual(self.ui.format_filesize(2560), '2.6 KB')
00204         self.assertEqual(self.ui.format_filesize(999999), '1000.0 KB')
00205         self.assertEqual(self.ui.format_filesize(1000000), '1.0 MB')
00206         self.assertEqual(self.ui.format_filesize(2.7 * 1000000), '2.7 MB')
00207         self.assertEqual(self.ui.format_filesize(1024 * 1000000), '1.0 GB')
00208         self.assertEqual(self.ui.format_filesize(2560 * 1000000), '2.6 GB')
00209 
00210     def test_get_size_loaded(self):
00211         '''get_complete_size() and get_reduced_size() for loaded Reports'''
00212 
00213         self.ui.load_report(self.report_file.name)
00214 
00215         fsize = os.path.getsize(self.report_file.name)
00216         complete_ratio = float(self.ui.get_complete_size()) / fsize
00217         self.assertTrue(complete_ratio >= 0.9 and complete_ratio <= 1.1)
00218 
00219         rs = self.ui.get_reduced_size()
00220         self.assertTrue(rs > 1000)
00221         self.assertTrue(rs < 10000)
00222 
00223         # now add some information (e. g. from package hooks)
00224         self.ui.report['ExtraInfo'] = 'A' * 50000
00225         s = self.ui.get_complete_size()
00226         self.assertTrue(s >= fsize + 49900)
00227         self.assertTrue(s < fsize + 60000)
00228 
00229         rs = self.ui.get_reduced_size()
00230         self.assertTrue(rs > 51000)
00231         self.assertTrue(rs < 60000)
00232 
00233     def test_get_size_constructed(self):
00234         '''get_complete_size() and get_reduced_size() for on-the-fly Reports'''
00235 
00236         self.ui.report = apport.Report('Bug')
00237         self.ui.report['Hello'] = 'World'
00238 
00239         s = self.ui.get_complete_size()
00240         self.assertTrue(s > 5)
00241         self.assertTrue(s < 100)
00242 
00243         self.assertEqual(s, self.ui.get_reduced_size())
00244 
00245     def test_load_report(self):
00246         '''load_report()'''
00247 
00248         # valid report
00249         self.ui.load_report(self.report_file.name)
00250         self.assertEqual(set(self.ui.report.keys()), set(self.report.keys()))
00251         self.assertEqual(self.ui.report['Package'], self.report['Package'])
00252         self.assertEqual(self.ui.report['CoreDump'].get_value(),
00253                          self.report['CoreDump'].get_value())
00254         self.assertEqual(self.ui.msg_title, None)
00255 
00256         # report without Package
00257         del self.report['Package']
00258         del self.report['SourcePackage']
00259         del self.report['ExecutablePath']
00260         self.update_report_file()
00261         self.ui.load_report(self.report_file.name)
00262 
00263         self.assertTrue(self.ui.report is None)
00264         self.assertEqual(self.ui.msg_title, _('Invalid problem report'))
00265         self.assertEqual(self.ui.msg_severity, 'info')
00266 
00267         self.ui.clear_msg()
00268 
00269         # invalid base64 encoding
00270         self.report_file.seek(0)
00271         self.report_file.truncate()
00272         self.report_file.write(b'''Type: test
00273 Package: foo 1-1
00274 CoreDump: base64
00275 bOgUs=
00276 ''')
00277         self.report_file.flush()
00278 
00279         self.ui.load_report(self.report_file.name)
00280         self.assertTrue(self.ui.report is None)
00281         self.assertEqual(self.ui.msg_title, _('Invalid problem report'))
00282         self.assertEqual(self.ui.msg_severity, 'error')
00283 
00284     def test_restart(self):
00285         '''restart()'''
00286 
00287         # test with only ProcCmdline
00288         p = os.path.join(apport.fileutils.report_dir, 'ProcCmdline')
00289         r = os.path.join(apport.fileutils.report_dir, 'Custom')
00290         self.report['ProcCmdline'] = 'touch ' + p
00291         self.update_report_file()
00292         self.ui.load_report(self.report_file.name)
00293 
00294         self.ui.restart()
00295         time.sleep(1)  # FIXME: race condition
00296         self.assertTrue(os.path.exists(p))
00297         self.assertTrue(not os.path.exists(r))
00298         os.unlink(p)
00299 
00300         # test with RespawnCommand
00301         self.report['RespawnCommand'] = 'touch ' + r
00302         self.update_report_file()
00303         self.ui.load_report(self.report_file.name)
00304 
00305         self.ui.restart()
00306         time.sleep(1)  # FIXME: race condition
00307         self.assertTrue(not os.path.exists(p))
00308         self.assertTrue(os.path.exists(r))
00309         os.unlink(r)
00310 
00311         # test that invalid command does not make us fall apart
00312         del self.report['RespawnCommand']
00313         self.report['ProcCmdline'] = '/nonexisting'
00314         self.update_report_file()
00315         self.ui.load_report(self.report_file.name)
00316 
00317     def test_collect_info_distro(self):
00318         '''collect_info() on report without information (distro bug)'''
00319 
00320         # report without any information (distro bug)
00321         self.ui.report = apport.Report()
00322         self.ui.collect_info()
00323         self.assertTrue(set(['Date', 'Uname', 'DistroRelease', 'ProblemType']).issubset(
00324             set(self.ui.report.keys())))
00325         self.assertEqual(self.ui.ic_progress_pulses, 0,
00326                          'no progress dialog for distro bug info collection')
00327 
00328     def test_collect_info_exepath(self):
00329         '''collect_info() on report with only ExecutablePath'''
00330 
00331         # report with only package information
00332         self.report = apport.Report('Bug')
00333         self.report['ExecutablePath'] = '/bin/bash'
00334         self.update_report_file()
00335         self.ui.load_report(self.report_file.name)
00336         # add some tuple values, for robustness testing (might be added by
00337         # apport hooks)
00338         self.ui.report['Fstab'] = ('/etc/fstab', True)
00339         self.ui.report['CompressedValue'] = problem_report.CompressedValue(b'Test')
00340         self.ui.collect_info()
00341         self.assertTrue(set(['SourcePackage', 'Package', 'ProblemType',
00342                              'Uname', 'Dependencies', 'DistroRelease', 'Date',
00343                              'ExecutablePath']).issubset(set(self.ui.report.keys())))
00344         self.assertTrue(self.ui.ic_progress_pulses > 0,
00345                         'progress dialog for package bug info collection')
00346         self.assertEqual(self.ui.ic_progress_active, False,
00347                          'progress dialog for package bug info collection finished')
00348 
00349     def test_collect_info_package(self):
00350         '''collect_info() on report with a package'''
00351 
00352         # report with only package information
00353         self.ui.report = apport.Report('Bug')
00354         self.ui.cur_package = 'bash'
00355         self.ui.collect_info()
00356         self.assertTrue(set(['SourcePackage', 'Package', 'ProblemType',
00357                              'Uname', 'Dependencies', 'DistroRelease',
00358                              'Date']).issubset(set(self.ui.report.keys())))
00359         self.assertTrue(self.ui.ic_progress_pulses > 0,
00360                         'progress dialog for package bug info collection')
00361         self.assertEqual(self.ui.ic_progress_active, False,
00362                          'progress dialog for package bug info collection finished')
00363 
00364     def test_collect_info_permissions(self):
00365         '''collect_info() leaves the report accessible to the group'''
00366 
00367         self.ui.report = apport.Report('Bug')
00368         self.ui.cur_package = 'bash'
00369         self.ui.report_file = self.report_file.name
00370         self.ui.collect_info()
00371         self.assertTrue(os.stat(self.report_file.name).st_mode & stat.S_IRGRP)
00372 
00373     def test_handle_duplicate(self):
00374         '''handle_duplicate()'''
00375 
00376         self.ui.load_report(self.report_file.name)
00377         self.assertEqual(self.ui.handle_duplicate(), False)
00378         self.assertEqual(self.ui.msg_title, None)
00379         self.assertEqual(self.ui.opened_url, None)
00380 
00381         demo_url = 'http://example.com/1'
00382         self.report['KnownReport'] = demo_url
00383         self.update_report_file()
00384         self.ui.load_report(self.report_file.name)
00385         self.assertEqual(self.ui.handle_duplicate(), True)
00386         self.assertEqual(self.ui.msg_severity, 'info')
00387         self.assertEqual(self.ui.opened_url, demo_url)
00388 
00389         self.ui.opened_url = None
00390         demo_url = 'http://example.com/1'
00391         self.report['KnownReport'] = '1'
00392         self.update_report_file()
00393         self.ui.load_report(self.report_file.name)
00394         self.assertEqual(self.ui.handle_duplicate(), True)
00395         self.assertEqual(self.ui.msg_severity, 'info')
00396         self.assertEqual(self.ui.opened_url, None)
00397 
00398     def test_run_nopending(self):
00399         '''running the frontend without any pending reports'''
00400 
00401         sys.argv = []
00402         self.ui = TestSuiteUserInterface()
00403         self.assertEqual(self.ui.run_argv(), False)
00404 
00405     def test_run_report_bug_noargs(self):
00406         '''run_report_bug() without specifying arguments'''
00407 
00408         sys.argv = ['ui-test', '-f']
00409         self.ui = TestSuiteUserInterface()
00410         self.assertEqual(self.ui.run_argv(), False)
00411         self.assertEqual(self.ui.msg_severity, 'error')
00412 
00413     def test_run_version(self):
00414         '''run_report_bug() as "ubuntu-bug" with version argument'''
00415 
00416         sys.argv = ['ubuntu-bug', '-v']
00417         self.ui = TestSuiteUserInterface()
00418         orig_stdout = sys.stdout
00419         sys.stdout = StringIO()
00420         self.assertEqual(self.ui.run_argv(), True)
00421         output = sys.stdout.getvalue()
00422         sys.stdout = orig_stdout
00423         self.assertEqual(output, apport.ui.__version__ + '\n')
00424 
00425     def test_run_report_bug_package(self):
00426         '''run_report_bug() for a package'''
00427 
00428         sys.argv = ['ui-test', '-f', '-p', 'bash']
00429         self.ui = TestSuiteUserInterface()
00430         self.ui.present_details_response = {'report': True,
00431                                             'blacklist': False,
00432                                             'examine': False,
00433                                             'restart': False}
00434         self.assertEqual(self.ui.run_argv(), True)
00435 
00436         self.assertEqual(self.ui.msg_severity, None)
00437         self.assertEqual(self.ui.msg_title, None)
00438         self.assertTrue(self.ui.present_details_shown)
00439         self.assertEqual(self.ui.opened_url, 'http://bash.bugs.example.com/%i' % self.ui.crashdb.latest_id())
00440 
00441         self.assertTrue(self.ui.ic_progress_pulses > 0)
00442         self.assertEqual(self.ui.report['SourcePackage'], 'bash')
00443         self.assertTrue('Dependencies' in self.ui.report.keys())
00444         self.assertTrue('ProcEnviron' in self.ui.report.keys())
00445         self.assertEqual(self.ui.report['ProblemType'], 'Bug')
00446 
00447         # should not crash on nonexisting package
00448         sys.argv = ['ui-test', '-f', '-p', 'nonexisting_gibberish']
00449         self.ui = TestSuiteUserInterface()
00450         self.ui.run_argv()
00451 
00452         self.assertEqual(self.ui.msg_severity, 'error')
00453 
00454     def test_run_report_bug_pid_tags(self):
00455         '''run_report_bug() for a pid with extra tags'''
00456 
00457         # fork a test process
00458         pid = os.fork()
00459         if pid == 0:
00460             os.dup2(os.open('/dev/null', os.O_WRONLY), sys.stdout.fileno())
00461             os.execv('/usr/bin/yes', ['yes'])
00462             assert False, 'Could not execute /usr/bin/yes'
00463 
00464         time.sleep(0.5)
00465 
00466         try:
00467             # report a bug on yes process
00468             sys.argv = ['ui-test', '-f', '--tag', 'foo', '-P', str(pid)]
00469             self.ui = TestSuiteUserInterface()
00470             self.ui.present_details_response = {'report': True,
00471                                                 'blacklist': False,
00472                                                 'examine': False,
00473                                                 'restart': False}
00474             self.assertEqual(self.ui.run_argv(), True)
00475         finally:
00476             # kill test process
00477             os.kill(pid, signal.SIGKILL)
00478             os.waitpid(pid, 0)
00479 
00480         self.assertTrue('SourcePackage' in self.ui.report.keys())
00481         self.assertTrue('Dependencies' in self.ui.report.keys())
00482         self.assertTrue('ProcMaps' in self.ui.report.keys())
00483         self.assertEqual(self.ui.report['ExecutablePath'], '/usr/bin/yes')
00484         self.assertFalse('ProcCmdline' in self.ui.report)  # privacy!
00485         self.assertTrue('ProcEnviron' in self.ui.report.keys())
00486         self.assertEqual(self.ui.report['ProblemType'], 'Bug')
00487         self.assertTrue('Tags' in self.ui.report.keys())
00488         self.assertTrue('foo' in self.ui.report['Tags'])
00489 
00490         self.assertEqual(self.ui.msg_severity, None)
00491         self.assertEqual(self.ui.msg_title, None)
00492         self.assertEqual(self.ui.opened_url, 'http://coreutils.bugs.example.com/%i' % self.ui.crashdb.latest_id())
00493         self.assertTrue(self.ui.present_details_shown)
00494         self.assertTrue(self.ui.ic_progress_pulses > 0)
00495 
00496     @classmethod
00497     def _find_unused_pid(klass):
00498         '''Find and return an unused PID'''
00499 
00500         pid = 1
00501         while True:
00502             pid += 1
00503             try:
00504                 os.kill(pid, 0)
00505             except OSError as e:
00506                 if e.errno == errno.ESRCH:
00507                     break
00508         return pid
00509 
00510     def test_run_report_bug_wrong_pid(self):
00511         '''run_report_bug() for a nonexisting pid'''
00512 
00513         # silently ignore missing PID; this happens when the user closes
00514         # the application prematurely
00515         pid = self._find_unused_pid()
00516         sys.argv = ['ui-test', '-f', '-P', str(pid)]
00517         self.ui = TestSuiteUserInterface()
00518         self.ui.run_argv()
00519 
00520     def test_run_report_bug_noperm_pid(self):
00521         '''run_report_bug() for a pid which runs as a different user'''
00522 
00523         restore_root = False
00524         if os.getuid() == 0:
00525             # temporarily drop to normal user "mail"
00526             os.setresuid(8, 8, -1)
00527             restore_root = True
00528 
00529         try:
00530             sys.argv = ['ui-test', '-f', '-P', '1']
00531             self.ui = TestSuiteUserInterface()
00532             self.ui.run_argv()
00533 
00534             self.assertEqual(self.ui.msg_severity, 'error')
00535         finally:
00536             if restore_root:
00537                 os.setresuid(0, 0, -1)
00538 
00539     def test_run_report_bug_unpackaged_pid(self):
00540         '''run_report_bug() for a pid of an unpackaged program'''
00541 
00542         # create unpackaged test program
00543         (fd, exename) = tempfile.mkstemp()
00544         with open('/usr/bin/yes', 'rb') as f:
00545             os.write(fd, f.read())
00546         os.close(fd)
00547         os.chmod(exename, 0o755)
00548 
00549         # unpackaged test process
00550         pid = os.fork()
00551         if pid == 0:
00552             os.dup2(os.open('/dev/null', os.O_WRONLY), sys.stdout.fileno())
00553             os.execv(exename, [exename])
00554 
00555         try:
00556             sys.argv = ['ui-test', '-f', '-P', str(pid)]
00557             self.ui = TestSuiteUserInterface()
00558             self.assertRaises(SystemExit, self.ui.run_argv)
00559         finally:
00560             os.kill(pid, signal.SIGKILL)
00561             os.wait()
00562             os.unlink(exename)
00563 
00564         self.assertEqual(self.ui.msg_severity, 'error')
00565 
00566     def test_run_report_bug_kernel_thread(self):
00567         '''run_report_bug() for a pid of a kernel thread'''
00568 
00569         pid = None
00570         for path in glob.glob('/proc/[0-9]*/stat'):
00571             with open(path) as f:
00572                 stat = f.read().split()
00573             flags = int(stat[8])
00574             if flags & apport.ui.PF_KTHREAD:
00575                 pid = int(stat[0])
00576                 break
00577 
00578         self.assertFalse(pid is None)
00579         sys.argv = ['ui-test', '-f', '-P', str(pid)]
00580         self.ui = TestSuiteUserInterface()
00581         self.ui.present_details_response = {'report': True,
00582                                             'blacklist': False,
00583                                             'examine': False,
00584                                             'restart': False}
00585         self.ui.run_argv()
00586 
00587         self.assertTrue(self.ui.report['Package'].startswith(apport.packaging.get_kernel_package()))
00588 
00589     def test_run_report_bug_file(self):
00590         '''run_report_bug() with saving report into a file'''
00591 
00592         d = os.path.join(apport.fileutils.report_dir, 'home')
00593         os.mkdir(d)
00594         reportfile = os.path.join(d, 'bashisbad.apport')
00595 
00596         sys.argv = ['ui-test', '-f', '-p', 'bash', '--save', reportfile]
00597         self.ui = TestSuiteUserInterface()
00598         self.assertEqual(self.ui.run_argv(), True)
00599 
00600         self.assertEqual(self.ui.msg_severity, None)
00601         self.assertEqual(self.ui.msg_title, None)
00602         self.assertEqual(self.ui.opened_url, None)
00603         self.assertFalse(self.ui.present_details_shown)
00604 
00605         self.assertTrue(self.ui.ic_progress_pulses > 0)
00606 
00607         r = apport.Report()
00608         with open(reportfile, 'rb') as f:
00609             r.load(f)
00610 
00611         self.assertEqual(r['SourcePackage'], 'bash')
00612         self.assertTrue('Dependencies' in r.keys())
00613         self.assertTrue('ProcEnviron' in r.keys())
00614         self.assertEqual(r['ProblemType'], 'Bug')
00615 
00616         # report it
00617         sys.argv = ['ui-test', '-c', reportfile]
00618         self.ui = TestSuiteUserInterface()
00619         self.ui.present_details_response = {'report': True,
00620                                             'blacklist': False,
00621                                             'examine': False,
00622                                             'restart': False}
00623         self.assertEqual(self.ui.run_argv(), True)
00624 
00625         self.assertEqual(self.ui.msg_text, None)
00626         self.assertEqual(self.ui.msg_severity, None)
00627         self.assertTrue(self.ui.present_details_shown)
00628 
00629     def _gen_test_crash(self):
00630         '''Generate a Report with real crash data'''
00631 
00632         # create a test executable
00633         test_executable = '/usr/bin/yes'
00634         assert os.access(test_executable, os.X_OK), test_executable + ' is not executable'
00635         pid = os.fork()
00636         if pid == 0:
00637             os.dup2(os.open('/dev/null', os.O_WRONLY), sys.stdout.fileno())
00638             sys.stdin.close()
00639             os.setsid()
00640             resource.setrlimit(resource.RLIMIT_CORE, (-1, -1))
00641             os.chdir(apport.fileutils.report_dir)
00642             os.execv(test_executable, [test_executable])
00643             assert False, 'Could not execute ' + test_executable
00644 
00645         time.sleep(0.5)
00646 
00647         # generate crash report
00648         r = apport.Report()
00649         r['ExecutablePath'] = test_executable
00650         r['Signal'] = '11'
00651         r.add_proc_info(pid)
00652         r.add_user_info()
00653 
00654         # generate a core dump
00655         coredump = os.path.join(apport.fileutils.report_dir, 'core')
00656         os.kill(pid, signal.SIGSEGV)
00657         os.waitpid(pid, 0)
00658         # Otherwise the core dump is empty.
00659         time.sleep(0.5)
00660         assert os.path.exists(coredump)
00661         r['CoreDump'] = (coredump,)
00662 
00663         return r
00664 
00665     def test_run_crash(self):
00666         '''run_crash()'''
00667 
00668         r = self._gen_test_crash()
00669 
00670         # write crash report
00671         report_file = os.path.join(apport.fileutils.report_dir, 'test.crash')
00672 
00673         # cancel crash notification dialog
00674         with open(report_file, 'wb') as f:
00675             r.write(f)
00676         self.ui = TestSuiteUserInterface()
00677         self.ui.present_details_response = {'report': False,
00678                                             'blacklist': False,
00679                                             'examine': False,
00680                                             'restart': False}
00681         self.ui.run_crash(report_file)
00682         self.assertEqual(self.ui.msg_severity, None)
00683         self.assertEqual(self.ui.msg_title, None)
00684         self.assertEqual(self.ui.opened_url, None)
00685         self.assertEqual(self.ui.ic_progress_pulses, 0)
00686 
00687         # report in crash notification dialog, send full report
00688         with open(report_file, 'wb') as f:
00689             r.write(f)
00690         self.ui = TestSuiteUserInterface()
00691         self.ui.present_details_response = {'report': True,
00692                                             'blacklist': False,
00693                                             'examine': False,
00694                                             'restart': False}
00695         self.ui.run_crash(report_file)
00696         self.assertEqual(self.ui.msg_severity, None, self.ui.msg_text)
00697         self.assertEqual(self.ui.msg_title, None)
00698         self.assertEqual(self.ui.opened_url, 'http://coreutils.bugs.example.com/%i' % self.ui.crashdb.latest_id())
00699         self.assertFalse(self.ui.ic_progress_active)
00700         self.assertNotEqual(self.ui.ic_progress_pulses, 0)
00701         self.assertTrue(self.ui.present_details_shown)
00702 
00703         self.assertTrue('SourcePackage' in self.ui.report.keys())
00704         self.assertTrue('Dependencies' in self.ui.report.keys())
00705         self.assertTrue('Stacktrace' in self.ui.report.keys())
00706         self.assertTrue('ProcEnviron' in self.ui.report.keys())
00707         self.assertFalse('ExecutableTimestamp' in self.ui.report.keys())
00708         self.assertFalse('StacktraceAddressSignature' in self.ui.report.keys())
00709         self.assertEqual(self.ui.report['ProblemType'], 'Crash')
00710         self.assertTrue(len(self.ui.report['CoreDump']) > 10000)
00711         self.assertTrue(self.ui.report['Title'].startswith('yes crashed with SIGSEGV'))
00712 
00713         # so far we did not blacklist, verify that
00714         self.assertTrue(not self.ui.report.check_ignored())
00715 
00716         # cancel crash notification dialog and blacklist
00717         with open(report_file, 'wb') as f:
00718             r.write(f)
00719         self.ui = TestSuiteUserInterface()
00720         self.ui.present_details_response = {'report': False,
00721                                             'blacklist': True,
00722                                             'examine': False,
00723                                             'restart': False}
00724         self.ui.run_crash(report_file)
00725         self.assertEqual(self.ui.msg_severity, None)
00726         self.assertEqual(self.ui.msg_title, None)
00727         self.assertEqual(self.ui.opened_url, None)
00728         self.assertEqual(self.ui.ic_progress_pulses, 0)
00729 
00730         self.assertTrue(self.ui.report.check_ignored())
00731 
00732     def test_run_crash_abort(self):
00733         '''run_crash() for an abort() without assertion message'''
00734 
00735         r = self._gen_test_crash()
00736         r['Signal'] = '6'
00737         report_file = os.path.join(apport.fileutils.report_dir, 'test.crash')
00738         with open(report_file, 'wb') as f:
00739             r.write(f)
00740 
00741         self.ui.present_details_response = {'report': True,
00742                                             'blacklist': False,
00743                                             'examine': False,
00744                                             'restart': False}
00745         self.ui.run_crash(report_file)
00746         self.assertEqual(self.ui.msg_severity, None, self.ui.msg_text)
00747 
00748         self.assertTrue('SourcePackage' in self.ui.report.keys())
00749         self.assertTrue('Dependencies' in self.ui.report.keys())
00750         self.assertTrue('Stacktrace' in self.ui.report.keys())
00751         self.assertTrue('ProcEnviron' in self.ui.report.keys())
00752         self.assertFalse('ExecutableTimestamp' in self.ui.report.keys())
00753         self.assertEqual(self.ui.report['Signal'], '6')
00754 
00755         # we disable the ABRT filtering, we want these crashes after all
00756         #self.assertTrue('assert' in self.ui.msg_text, '%s: %s' %
00757         #    (self.ui.msg_title, self.ui.msg_text))
00758         #self.assertEqual(self.ui.msg_severity, 'info')
00759         self.assertEqual(self.ui.msg_severity, None)
00760         self.assertTrue(self.ui.present_details_shown)
00761 
00762     def test_run_crash_broken(self):
00763         '''run_crash() for an invalid core dump'''
00764 
00765         # generate broken crash report
00766         r = apport.Report()
00767         r['ExecutablePath'] = '/usr/bin/yes'
00768         r['Signal'] = '11'
00769         r['CoreDump'] = problem_report.CompressedValue()
00770         r['CoreDump'].gzipvalue = b'AAAAAAAA'
00771         r.add_user_info()
00772 
00773         report_file = os.path.join(apport.fileutils.report_dir, 'test.crash')
00774         with open(report_file, 'wb') as f:
00775             r.write(f)
00776 
00777         self.ui.present_details_response = {'report': True,
00778                                             'blacklist': False,
00779                                             'examine': False,
00780                                             'restart': False}
00781         self.ui.run_crash(report_file)
00782         self.assertEqual(self.ui.msg_severity, 'error', self.ui.msg_text)
00783         self.assertTrue('decompress' in self.ui.msg_text)
00784         self.assertTrue(self.ui.present_details_shown)
00785 
00786     def test_run_crash_argv_file(self):
00787         '''run_crash() through a file specified on the command line'''
00788 
00789         # valid
00790         self.report['Package'] = 'bash'
00791         self.update_report_file()
00792 
00793         sys.argv = ['ui-test', '-c', self.report_file.name]
00794         self.ui = TestSuiteUserInterface()
00795         self.ui.present_details_response = {'report': True,
00796                                             'blacklist': False,
00797                                             'examine': False,
00798                                             'restart': False}
00799 
00800         self.assertEqual(self.ui.run_argv(), True)
00801 
00802         self.assertEqual(self.ui.msg_text, None)
00803         self.assertEqual(self.ui.msg_severity, None)
00804         self.assertTrue(self.ui.present_details_shown)
00805 
00806         # unreportable
00807         self.report['Package'] = 'bash'
00808         self.report['UnreportableReason'] = b'It stinks. \xe2\x99\xa5'.decode('UTF-8')
00809         self.update_report_file()
00810 
00811         sys.argv = ['ui-test', '-c', self.report_file.name]
00812         self.ui = TestSuiteUserInterface()
00813         self.ui.present_details_response = {'report': True,
00814                                             'blacklist': False,
00815                                             'examine': False,
00816                                             'restart': False}
00817         self.assertEqual(self.ui.run_argv(), True)
00818 
00819         self.assertTrue('It stinks.' in self.ui.msg_text, '%s: %s' %
00820                         (self.ui.msg_title, self.ui.msg_text))
00821         self.assertEqual(self.ui.msg_severity, 'info')
00822 
00823         # should not die with an exception on an invalid name
00824         sys.argv = ['ui-test', '-c', '/nonexisting.crash']
00825         self.ui = TestSuiteUserInterface()
00826         self.assertEqual(self.ui.run_argv(), True)
00827         self.assertEqual(self.ui.msg_severity, 'error')
00828 
00829     def test_run_crash_unreportable(self):
00830         '''run_crash() on a crash with the UnreportableReason field'''
00831 
00832         self.report['UnreportableReason'] = 'It stinks.'
00833         self.report['ExecutablePath'] = '/bin/bash'
00834         self.report['Package'] = 'bash 1'
00835         self.update_report_file()
00836         self.ui.present_details_response = {'report': True,
00837                                             'blacklist': False,
00838                                             'examine': False,
00839                                             'restart': False}
00840 
00841         self.ui.run_crash(self.report_file.name)
00842 
00843         self.assertTrue('It stinks.' in self.ui.msg_text, '%s: %s' %
00844                         (self.ui.msg_title, self.ui.msg_text))
00845         self.assertEqual(self.ui.msg_severity, 'info')
00846 
00847     def test_run_crash_ignore(self):
00848         '''run_crash() on a crash with the Ignore field'''
00849         self.report['Ignore'] = 'True'
00850         self.report['ExecutablePath'] = '/bin/bash'
00851         self.report['Package'] = 'bash 1'
00852         self.update_report_file()
00853 
00854         self.ui.run_crash(self.report_file.name)
00855         self.assertEqual(self.ui.msg_severity, None)
00856 
00857     def test_run_crash_nocore(self):
00858         '''run_crash() for a crash dump without CoreDump'''
00859 
00860         # create a test executable
00861         test_executable = '/usr/bin/yes'
00862         assert os.access(test_executable, os.X_OK), test_executable + ' is not executable'
00863         pid = os.fork()
00864         if pid == 0:
00865             os.setsid()
00866             os.dup2(os.open('/dev/null', os.O_WRONLY), sys.stdout.fileno())
00867             os.execv(test_executable, [test_executable])
00868             assert False, 'Could not execute ' + test_executable
00869 
00870         try:
00871             time.sleep(0.5)
00872             # generate crash report
00873             r = apport.Report()
00874             r['ExecutablePath'] = test_executable
00875             r['Signal'] = '42'
00876             r.add_proc_info(pid)
00877             r.add_user_info()
00878         finally:
00879             # kill test executable
00880             os.kill(pid, signal.SIGKILL)
00881             os.waitpid(pid, 0)
00882 
00883         # write crash report
00884         report_file = os.path.join(apport.fileutils.report_dir, 'test.crash')
00885         with open(report_file, 'wb') as f:
00886             r.write(f)
00887 
00888         # run
00889         self.ui = TestSuiteUserInterface()
00890         self.ui.run_crash(report_file)
00891         self.assertEqual(self.ui.msg_severity, 'error')
00892         self.assertTrue('memory' in self.ui.msg_text, '%s: %s' %
00893                         (self.ui.msg_title, self.ui.msg_text))
00894 
00895     def test_run_crash_preretraced(self):
00896         '''run_crash() pre-retraced reports.
00897 
00898         This happens with crashes which are pre-processed by
00899         apport-retrace.
00900         '''
00901         r = self._gen_test_crash()
00902 
00903         #  effect of apport-retrace -c
00904         r.add_gdb_info()
00905         del r['CoreDump']
00906 
00907         # write crash report
00908         report_file = os.path.join(apport.fileutils.report_dir, 'test.crash')
00909 
00910         # report in crash notification dialog, cancel details report
00911         with open(report_file, 'wb') as f:
00912             r.write(f)
00913         self.ui = TestSuiteUserInterface()
00914         self.ui.present_details_response = {'report': False,
00915                                             'blacklist': False,
00916                                             'examine': False,
00917                                             'restart': False}
00918         self.ui.run_crash(report_file)
00919         self.assertEqual(self.ui.msg_severity, None, 'has %s message: %s: %s' % (
00920             self.ui.msg_severity, str(self.ui.msg_title), str(self.ui.msg_text)))
00921         self.assertEqual(self.ui.msg_title, None)
00922         self.assertTrue(self.ui.present_details_shown)
00923 
00924     def test_run_crash_precollected(self):
00925         '''run_crash() on complete report on uninstalled package
00926 
00927         This happens when reporting a problem from a different machine through
00928         copying a .crash file.
00929         '''
00930         self.ui.report = self._gen_test_crash()
00931         self.ui.collect_info()
00932 
00933         # now pretend to move it to a machine where the package is not
00934         # installed
00935         self.ui.report['Package'] = 'uninstalled_pkg 1'
00936 
00937         # write crash report
00938         report_file = os.path.join(apport.fileutils.report_dir, 'test.crash')
00939         with open(report_file, 'wb') as f:
00940             self.ui.report.write(f)
00941 
00942         # report it
00943         self.ui = TestSuiteUserInterface()
00944         self.ui.present_details_response = {'report': True,
00945                                             'blacklist': False,
00946                                             'examine': False,
00947                                             'restart': False}
00948         self.ui.run_crash(report_file)
00949         self.assertEqual(self.ui.cur_package, 'uninstalled_pkg')
00950         self.assertEqual(self.ui.msg_severity, None, 'has %s message: %s: %s' % (
00951             self.ui.msg_severity, str(self.ui.msg_title), str(self.ui.msg_text)))
00952         self.assertTrue(self.ui.opened_url.startswith('http://coreutils.bugs.example.com'))
00953         self.assertTrue(self.ui.present_details_shown)
00954 
00955     def test_run_crash_errors(self):
00956         '''run_crash() on various error conditions'''
00957 
00958         # crash report with invalid Package name
00959         r = apport.Report()
00960         r['ExecutablePath'] = '/bin/bash'
00961         r['Package'] = 'foobarbaz'
00962         report_file = os.path.join(apport.fileutils.report_dir, 'test.crash')
00963         with open(report_file, 'wb') as f:
00964             r.write(f)
00965 
00966         self.ui.present_details_response = {'report': True,
00967                                             'blacklist': False,
00968                                             'examine': False,
00969                                             'restart': False}
00970         self.ui.run_crash(report_file)
00971 
00972         self.assertEqual(self.ui.msg_title, _('Invalid problem report'))
00973         self.assertEqual(self.ui.msg_severity, 'error')
00974 
00975     def test_run_crash_uninstalled(self):
00976         '''run_crash() on reports with subsequently uninstalled packages'''
00977 
00978         # program got uninstalled between crash and report
00979         r = self._gen_test_crash()
00980         r['ExecutablePath'] = '/bin/nonexisting'
00981         r['Package'] = 'bash'
00982         report_file = os.path.join(apport.fileutils.report_dir, 'test.crash')
00983         with open(report_file, 'wb') as f:
00984             r.write(f)
00985 
00986         self.ui.present_details_response = {'report': True,
00987                                             'blacklist': False,
00988                                             'examine': False,
00989                                             'restart': False}
00990         self.ui.run_crash(report_file)
00991 
00992         self.assertEqual(self.ui.msg_title, _('Invalid problem report'))
00993         self.assertEqual(self.ui.msg_severity, 'info')
00994 
00995         # interpreted program got uninstalled between crash and report
00996         r = apport.Report()
00997         r['ExecutablePath'] = '/bin/nonexisting'
00998         r['InterpreterPath'] = '/usr/bin/python'
00999         r['Traceback'] = 'ZeroDivisionError: integer division or modulo by zero'
01000 
01001         self.ui.run_crash(report_file)
01002 
01003         self.assertEqual(self.ui.msg_title, _('Invalid problem report'))
01004         self.assertEqual(self.ui.msg_severity, 'info')
01005 
01006         # interpreter got uninstalled between crash and report
01007         r = apport.Report()
01008         r['ExecutablePath'] = '/bin/sh'
01009         r['InterpreterPath'] = '/usr/bin/nonexisting'
01010         r['Traceback'] = 'ZeroDivisionError: integer division or modulo by zero'
01011 
01012         self.ui.run_crash(report_file)
01013 
01014         self.assertEqual(self.ui.msg_title, _('Invalid problem report'))
01015         self.assertEqual(self.ui.msg_severity, 'info')
01016 
01017     def test_run_crash_updated_binary(self):
01018         '''run_crash() on binary that got updated in the meantime'''
01019 
01020         r = self._gen_test_crash()
01021         r['ExecutableTimestamp'] = str(int(r['ExecutableTimestamp']) - 10)
01022         report_file = os.path.join(apport.fileutils.report_dir, 'test.crash')
01023         with open(report_file, 'wb') as f:
01024             r.write(f)
01025 
01026         self.ui.present_details_response = {'report': True,
01027                                             'blacklist': False,
01028                                             'examine': False,
01029                                             'restart': False}
01030         self.ui.run_crash(report_file)
01031 
01032         self.assertFalse('ExecutableTimestamp' in self.ui.report)
01033         self.assertTrue(self.ui.report['ExecutablePath'] in self.ui.msg_text, '%s: %s' %
01034                         (self.ui.msg_title, self.ui.msg_text))
01035         self.assertTrue('changed' in self.ui.msg_text, '%s: %s' %
01036                         (self.ui.msg_title, self.ui.msg_text))
01037         self.assertEqual(self.ui.msg_severity, 'info')
01038 
01039     def test_run_crash_package(self):
01040         '''run_crash() for a package error'''
01041 
01042         # generate crash report
01043         r = apport.Report('Package')
01044         r['Package'] = 'bash'
01045         r['SourcePackage'] = 'bash'
01046         r['ErrorMessage'] = 'It broke'
01047         r['VarLogPackagerlog'] = 'foo\nbar'
01048         r.add_os_info()
01049 
01050         # write crash report
01051         report_file = os.path.join(apport.fileutils.report_dir, 'test.crash')
01052 
01053         # cancel crash notification dialog
01054         with open(report_file, 'wb') as f:
01055             r.write(f)
01056         self.ui = TestSuiteUserInterface()
01057         self.ui.present_details_response = {'report': False,
01058                                             'blacklist': False,
01059                                             'examine': False,
01060                                             'restart': False}
01061         self.ui.run_crash(report_file)
01062         self.assertEqual(self.ui.msg_severity, None)
01063         self.assertEqual(self.ui.msg_title, None)
01064         self.assertEqual(self.ui.opened_url, None)
01065         self.assertEqual(self.ui.ic_progress_pulses, 0)
01066         self.assertTrue(self.ui.present_details_shown)
01067 
01068         # report in crash notification dialog, send report
01069         with open(report_file, 'wb') as f:
01070             r.write(f)
01071         self.ui = TestSuiteUserInterface()
01072         self.ui.present_details_response = {'report': True,
01073                                             'blacklist': False,
01074                                             'examine': False,
01075                                             'restart': False}
01076         self.ui.run_crash(report_file)
01077         self.assertEqual(self.ui.msg_severity, None)
01078         self.assertEqual(self.ui.msg_title, None)
01079         self.assertEqual(self.ui.opened_url, 'http://bash.bugs.example.com/%i' % self.ui.crashdb.latest_id())
01080         self.assertTrue(self.ui.present_details_shown)
01081 
01082         self.assertTrue('SourcePackage' in self.ui.report.keys())
01083         self.assertTrue('Package' in self.ui.report.keys())
01084         self.assertEqual(self.ui.report['ProblemType'], 'Package')
01085 
01086         # verify that additional information has been collected
01087         self.assertTrue('Architecture' in self.ui.report.keys())
01088         self.assertTrue('DistroRelease' in self.ui.report.keys())
01089         self.assertTrue('Uname' in self.ui.report.keys())
01090 
01091     def test_run_crash_kernel(self):
01092         '''run_crash() for a kernel error'''
01093 
01094         # set up hook
01095         f = open(os.path.join(self.hookdir, 'source_linux.py'), 'w')
01096         f.write('''def add_info(report, ui):
01097     report['KernelDebug'] = 'LotsMoreInfo'
01098 ''')
01099         f.close()
01100 
01101         # generate crash report
01102         r = apport.Report('KernelCrash')
01103         r['Package'] = apport.packaging.get_kernel_package()
01104         r['SourcePackage'] = 'linux'
01105 
01106         # write crash report
01107         report_file = os.path.join(apport.fileutils.report_dir, 'test.crash')
01108 
01109         # cancel crash notification dialog
01110         with open(report_file, 'wb') as f:
01111             r.write(f)
01112         self.ui = TestSuiteUserInterface()
01113         self.ui.present_details_response = {'report': False,
01114                                             'blacklist': False,
01115                                             'examine': False,
01116                                             'restart': False}
01117         self.ui.run_crash(report_file)
01118         self.assertEqual(self.ui.msg_severity, None, 'error: %s - %s' %
01119                          (self.ui.msg_title, self.ui.msg_text))
01120         self.assertEqual(self.ui.msg_title, None)
01121         self.assertEqual(self.ui.opened_url, None)
01122         self.assertEqual(self.ui.ic_progress_pulses, 0)
01123         self.assertTrue(self.ui.present_details_shown)
01124 
01125         # report in crash notification dialog, send report
01126         with open(report_file, 'wb') as f:
01127             r.write(f)
01128         self.ui = TestSuiteUserInterface()
01129         self.ui.present_details_response = {'report': True,
01130                                             'blacklist': False,
01131                                             'examine': False,
01132                                             'restart': False}
01133         self.ui.run_crash(report_file)
01134         self.assertEqual(self.ui.msg_severity, None, str(self.ui.msg_title) +
01135                          ' ' + str(self.ui.msg_text))
01136         self.assertEqual(self.ui.msg_title, None)
01137         self.assertEqual(self.ui.opened_url, 'http://linux.bugs.example.com/%i' % self.ui.crashdb.latest_id())
01138         self.assertTrue(self.ui.present_details_shown)
01139 
01140         self.assertTrue('SourcePackage' in self.ui.report.keys())
01141         # did we run the hooks properly?
01142         self.assertTrue('KernelDebug' in self.ui.report.keys())
01143         self.assertEqual(self.ui.report['ProblemType'], 'KernelCrash')
01144 
01145     def test_run_crash_anonymity(self):
01146         '''run_crash() anonymization'''
01147 
01148         r = self._gen_test_crash()
01149         utf8_val = b'\xc3\xa4 ' + os.uname()[1].encode('UTF-8') + b' \xe2\x99\xa5 '
01150         r['ProcUnicodeValue'] = utf8_val.decode('UTF-8')
01151         r['ProcByteArrayValue'] = utf8_val
01152         report_file = os.path.join(apport.fileutils.report_dir, 'test.crash')
01153         with open(report_file, 'wb') as f:
01154             r.write(f)
01155         self.ui = TestSuiteUserInterface()
01156         self.ui.present_details_response = {'report': True,
01157                                             'blacklist': False,
01158                                             'examine': False,
01159                                             'restart': False}
01160         self.ui.run_crash(report_file)
01161         self.assertEqual(self.ui.msg_severity, None, self.ui.msg_text)
01162 
01163         self.assertFalse('ProcCwd' in self.ui.report)
01164 
01165         dump = BytesIO()
01166         self.ui.report.write(dump)
01167         report = dump.getvalue().decode('UTF-8')
01168 
01169         p = pwd.getpwuid(os.getuid())
01170         bad_strings = [os.uname()[1], p[0], p[4], p[5], os.getcwd()]
01171 
01172         for s in bad_strings:
01173             self.assertFalse(s in report, 'dump contains sensitive string: %s' % s)
01174 
01175     def test_run_crash_anonymity_order(self):
01176         '''run_crash() anonymization runs after info and duplicate collection'''
01177 
01178         # pretend the hostname looks like a hex number which matches
01179         # the stack trace address
01180         uname = os.uname()
01181         uname = (uname[0], '0xDEADBEEF', uname[2], uname[3], uname[4])
01182         orig_uname = os.uname
01183         orig_add_gdb_info = apport.report.Report.add_gdb_info
01184         os.uname = lambda: uname
01185 
01186         def fake_add_gdb_info(self):
01187             self['Stacktrace'] = '''#0  0xDEADBEEF in h (p=0x0) at crash.c:25
01188 #1  0x10000042 in g (x=1, y=42) at crash.c:26
01189 #1  0x10000001 in main () at crash.c:40
01190 '''
01191             self['ProcMaps'] = '''
01192 10000000-DEADBEF0 r-xp 00000000 08:02 100000           /bin/crash
01193 '''
01194             assert self.crash_signature_addresses() is not None
01195 
01196         try:
01197             r = self._gen_test_crash()
01198             apport.report.Report.add_gdb_info = fake_add_gdb_info
01199             r['ProcAuxInfo'] = 'my 0xDEADBEEF'
01200             report_file = os.path.join(apport.fileutils.report_dir, 'test.crash')
01201             with open(report_file, 'wb') as f:
01202                 r.write(f)
01203 
01204             # if this runs anonymization before the duplicate signature, then this
01205             # will fail, as 0xDEADhostname is an invalid address
01206             self.ui = TestSuiteUserInterface()
01207             self.ui.present_details_response = {'report': True,
01208                                                 'blacklist': False,
01209                                                 'examine': False,
01210                                                 'restart': False}
01211             self.ui.run_crash(report_file)
01212             self.assertEqual(self.ui.msg_severity, None, self.ui.msg_text)
01213 
01214             self.assertEqual(self.ui.report['ProcAuxInfo'], 'my hostname')
01215             # after anonymization this should mess up Stacktrace; this mostly
01216             # confirms that our test logic works
01217             self.assertEqual(self.ui.report.crash_signature_addresses(), None)
01218         finally:
01219             os.uname = orig_uname
01220             apport.report.Report.add_gdb_info = orig_add_gdb_info
01221 
01222     def test_run_crash_anonymity_substring(self):
01223         '''run_crash() anonymization only catches whole words'''
01224 
01225         # pretend the hostname is "ed", a substring of e. g. "crashed"
01226         uname = os.uname()
01227         uname = (uname[0], 'ed', uname[2], uname[3], uname[4])
01228         orig_uname = os.uname
01229         os.uname = lambda: uname
01230 
01231         try:
01232             r = self._gen_test_crash()
01233             r['ProcInfo1'] = 'my ed'
01234             r['ProcInfo2'] = '"ed.localnet"'
01235             r['ProcInfo3'] = 'education'
01236             report_file = os.path.join(apport.fileutils.report_dir, 'test.crash')
01237             with open(report_file, 'wb') as f:
01238                 r.write(f)
01239 
01240             self.ui = TestSuiteUserInterface()
01241             self.ui.present_details_response = {'report': True,
01242                                                 'blacklist': False,
01243                                                 'examine': False,
01244                                                 'restart': False}
01245             self.ui.run_crash(report_file)
01246             self.assertEqual(self.ui.msg_severity, None, self.ui.msg_text)
01247 
01248             self.assertTrue(self.ui.report['Title'].startswith('yes crashed with SIGSEGV'),
01249                             self.ui.report['Title'])
01250             self.assertEqual(self.ui.report['ProcInfo1'], 'my hostname')
01251             self.assertEqual(self.ui.report['ProcInfo2'], '"hostname.localnet"')
01252             self.assertEqual(self.ui.report['ProcInfo3'], 'education')
01253         finally:
01254             os.uname = orig_uname
01255 
01256     def test_run_crash_known(self):
01257         '''run_crash() for already known problem'''
01258 
01259         r = self._gen_test_crash()
01260         report_file = os.path.join(apport.fileutils.report_dir, 'test.crash')
01261         self.ui = TestSuiteUserInterface()
01262         self.ui.present_details_response = {'report': True,
01263                                             'blacklist': False,
01264                                             'examine': False,
01265                                             'restart': False}
01266 
01267         # known without URL
01268         with open(report_file, 'wb') as f:
01269             r.write(f)
01270         self.ui.crashdb.known = lambda r: True
01271         self.ui.run_crash(report_file)
01272         self.assertEqual(self.ui.report['KnownReport'], '1')
01273         self.assertEqual(self.ui.msg_severity, 'info')
01274         self.assertEqual(self.ui.opened_url, None)
01275 
01276         self.ui = TestSuiteUserInterface()
01277         self.ui.present_details_response = {'report': True,
01278                                             'blacklist': False,
01279                                             'examine': False,
01280                                             'restart': False}
01281         # known with URL
01282         with open(report_file, 'wb') as f:
01283             r.write(f)
01284         self.ui.crashdb.known = lambda r: 'http://myreport/1'
01285         self.ui.run_crash(report_file)
01286         self.assertEqual(self.ui.report['KnownReport'], 'http://myreport/1')
01287         self.assertEqual(self.ui.msg_severity, 'info')
01288         self.assertEqual(self.ui.opened_url, 'http://myreport/1')
01289 
01290     def test_run_update_report_nonexisting_package_from_bug(self):
01291         '''run_update_report() on a nonexisting package (from bug)'''
01292 
01293         sys.argv = ['ui-test', '-u', '1']
01294         self.ui = TestSuiteUserInterface()
01295 
01296         self.assertEqual(self.ui.run_argv(), False)
01297         self.assertTrue('No additional information collected.' in self.ui.msg_text)
01298         self.assertFalse(self.ui.present_details_shown)
01299 
01300     def test_run_update_report_nonexisting_package_cli(self):
01301         '''run_update_report() on a nonexisting package (CLI argument)'''
01302 
01303         sys.argv = ['ui-test', '-u', '1', '-p', 'bar']
01304         self.ui = TestSuiteUserInterface()
01305 
01306         self.assertEqual(self.ui.run_argv(), False)
01307         self.assertTrue('No additional information collected.' in self.ui.msg_text)
01308         self.assertFalse(self.ui.present_details_shown)
01309 
01310     def test_run_update_report_existing_package_from_bug(self):
01311         '''run_update_report() on an existing package (from bug)'''
01312 
01313         sys.argv = ['ui-test', '-u', '1']
01314         self.ui = TestSuiteUserInterface()
01315         self.ui.present_details_response = {'report': True,
01316                                             'blacklist': False,
01317                                             'examine': False,
01318                                             'restart': False}
01319 
01320         self.ui.crashdb.download(1)['SourcePackage'] = 'bash'
01321         self.ui.crashdb.download(1)['Package'] = 'bash'
01322         self.assertEqual(self.ui.run_argv(), True)
01323         self.assertEqual(self.ui.msg_severity, None, self.ui.msg_text)
01324         self.assertEqual(self.ui.msg_title, None)
01325         self.assertEqual(self.ui.opened_url, None)
01326         self.assertTrue(self.ui.present_details_shown)
01327 
01328         self.assertTrue(self.ui.ic_progress_pulses > 0)
01329         self.assertTrue(self.ui.report['Package'].startswith('bash '))
01330         self.assertTrue('Dependencies' in self.ui.report.keys())
01331         self.assertTrue('ProcEnviron' in self.ui.report.keys())
01332 
01333     def test_run_update_report_existing_package_cli_tags(self):
01334         '''run_update_report() on an existing package (CLI argument) with extra tag'''
01335 
01336         sys.argv = ['ui-test', '-u', '1', '-p', 'bash', '--tag', 'foo']
01337         self.ui = TestSuiteUserInterface()
01338         self.ui.present_details_response = {'report': True,
01339                                             'blacklist': False,
01340                                             'examine': False,
01341                                             'restart': False}
01342 
01343         self.assertEqual(self.ui.run_argv(), True)
01344         self.assertEqual(self.ui.msg_severity, None, self.ui.msg_text)
01345         self.assertEqual(self.ui.msg_title, None)
01346         self.assertEqual(self.ui.opened_url, None)
01347         self.assertTrue(self.ui.present_details_shown)
01348 
01349         self.assertTrue(self.ui.ic_progress_pulses > 0)
01350         self.assertTrue(self.ui.report['Package'].startswith('bash '))
01351         self.assertTrue('Dependencies' in self.ui.report.keys())
01352         self.assertTrue('ProcEnviron' in self.ui.report.keys())
01353         self.assertTrue('foo' in self.ui.report['Tags'])
01354 
01355     def test_run_update_report_existing_package_cli_cmdname(self):
01356         '''run_update_report() on an existing package (-collect program)'''
01357 
01358         sys.argv = ['apport-collect', '-p', 'bash', '1']
01359         self.ui = TestSuiteUserInterface()
01360         self.ui.present_details_response = {'report': True,
01361                                             'blacklist': False,
01362                                             'examine': False,
01363                                             'restart': False}
01364 
01365         self.assertEqual(self.ui.run_argv(), True)
01366         self.assertEqual(self.ui.msg_severity, None, self.ui.msg_text)
01367         self.assertEqual(self.ui.msg_title, None)
01368         self.assertEqual(self.ui.opened_url, None)
01369         self.assertTrue(self.ui.present_details_shown)
01370 
01371         self.assertTrue(self.ui.ic_progress_pulses > 0)
01372         self.assertTrue(self.ui.report['Package'].startswith('bash '))
01373         self.assertTrue('Dependencies' in self.ui.report.keys())
01374         self.assertTrue('ProcEnviron' in self.ui.report.keys())
01375 
01376     def test_run_update_report_noninstalled_but_hook(self):
01377         '''run_update_report() on an uninstalled package with a source hook'''
01378 
01379         sys.argv = ['ui-test', '-u', '1']
01380         self.ui = TestSuiteUserInterface()
01381         self.ui.present_details_response = {'report': True,
01382                                             'blacklist': False,
01383                                             'examine': False,
01384                                             'restart': False}
01385 
01386         with open(os.path.join(self.hookdir, 'source_foo.py'), 'w') as f:
01387             f.write('def add_info(r, ui):\n  r["MachineType"]="Laptop"\n')
01388 
01389         self.assertEqual(self.ui.run_argv(), True, self.ui.report)
01390         self.assertEqual(self.ui.msg_severity, None, self.ui.msg_text)
01391         self.assertEqual(self.ui.msg_title, None)
01392         self.assertEqual(self.ui.opened_url, None)
01393         self.assertTrue(self.ui.present_details_shown)
01394 
01395         self.assertTrue(self.ui.ic_progress_pulses > 0)
01396         self.assertEqual(self.ui.report['Package'], 'foo (not installed)')
01397         self.assertEqual(self.ui.report['MachineType'], 'Laptop')
01398         self.assertTrue('ProcEnviron' in self.ui.report.keys())
01399 
01400     def test_run_update_report_different_binary_source(self):
01401         '''run_update_report() on a source package which does not have a binary of the same name'''
01402 
01403         kernel_pkg = apport.packaging.get_kernel_package()
01404         kernel_src = apport.packaging.get_source(kernel_pkg)
01405         self.assertNotEqual(kernel_pkg, kernel_src,
01406                             'this test assumes that the kernel binary package != kernel source package')
01407         self.assertNotEqual(apport.packaging.get_version(kernel_pkg), '',
01408                             'this test assumes that the kernel binary package %s is installed' % kernel_pkg)
01409 
01410         # this test assumes that the kernel source package name is not an
01411         # installed binary package
01412         self.assertRaises(ValueError, apport.packaging.get_version, kernel_src)
01413 
01414         sys.argv = ['ui-test', '-p', kernel_src, '-u', '1']
01415         self.ui = TestSuiteUserInterface()
01416         self.ui.present_details_response = {'report': True,
01417                                             'blacklist': False,
01418                                             'examine': False,
01419                                             'restart': False}
01420 
01421         with open(os.path.join(self.hookdir, 'source_%s.py' % kernel_src), 'w') as f:
01422             f.write('def add_info(r, ui):\n  r["MachineType"]="Laptop"\n')
01423 
01424         self.assertEqual(self.ui.run_argv(), True, self.ui.report)
01425         self.assertEqual(self.ui.msg_severity, None, self.ui.msg_text)
01426         self.assertEqual(self.ui.msg_title, None)
01427         self.assertEqual(self.ui.opened_url, None)
01428         self.assertTrue(self.ui.present_details_shown)
01429 
01430         self.assertTrue(self.ui.ic_progress_pulses > 0)
01431         self.assertEqual(self.ui.report['Package'], '%s (not installed)' % kernel_src)
01432         self.assertEqual(self.ui.report['MachineType'], 'Laptop')
01433         self.assertTrue('ProcEnviron' in self.ui.report.keys())
01434 
01435     def _run_hook(self, code):
01436         f = open(os.path.join(self.hookdir, 'coreutils.py'), 'w')
01437         f.write('def add_info(report, ui):\n%s\n' %
01438                 '\n'.join(['    ' + l for l in code.splitlines()]))
01439         f.close()
01440         self.ui.options.package = 'coreutils'
01441         self.ui.run_report_bug()
01442 
01443     def test_interactive_hooks_information(self):
01444         '''interactive hooks: HookUI.information()'''
01445 
01446         self.ui.present_details_response = {'report': False,
01447                                             'blacklist': False,
01448                                             'examine': False,
01449                                             'restart': False}
01450         self._run_hook('''report['begin'] = '1'
01451 ui.information('InfoText')
01452 report['end'] = '1'
01453 ''')
01454         self.assertEqual(self.ui.report['begin'], '1')
01455         self.assertEqual(self.ui.report['end'], '1')
01456         self.assertEqual(self.ui.msg_text, 'InfoText')
01457 
01458     def test_interactive_hooks_yesno(self):
01459         '''interactive hooks: HookUI.yesno()'''
01460 
01461         self.ui.present_details_response = {'report': False,
01462                                             'blacklist': False,
01463                                             'examine': False,
01464                                             'restart': False}
01465         self.ui.question_yesno_response = True
01466         self._run_hook('''report['begin'] = '1'
01467 report['answer'] = str(ui.yesno('YesNo?'))
01468 report['end'] = '1'
01469 ''')
01470         self.assertEqual(self.ui.report['begin'], '1')
01471         self.assertEqual(self.ui.report['end'], '1')
01472         self.assertEqual(self.ui.msg_text, 'YesNo?')
01473         self.assertEqual(self.ui.report['answer'], 'True')
01474 
01475         self.ui.question_yesno_response = False
01476         self.ui.run_report_bug()
01477         self.assertEqual(self.ui.report['answer'], 'False')
01478         self.assertEqual(self.ui.report['end'], '1')
01479 
01480         self.ui.question_yesno_response = None
01481         self.ui.run_report_bug()
01482         self.assertEqual(self.ui.report['answer'], 'None')
01483         self.assertEqual(self.ui.report['end'], '1')
01484 
01485     def test_interactive_hooks_file(self):
01486         '''interactive hooks: HookUI.file()'''
01487 
01488         self.ui.present_details_response = {'report': False,
01489                                             'blacklist': False,
01490                                             'examine': False,
01491                                             'restart': False}
01492         self.ui.question_file_response = '/etc/fstab'
01493         self._run_hook('''report['begin'] = '1'
01494 report['answer'] = str(ui.file('YourFile?'))
01495 report['end'] = '1'
01496 ''')
01497         self.assertEqual(self.ui.report['begin'], '1')
01498         self.assertEqual(self.ui.report['end'], '1')
01499         self.assertEqual(self.ui.msg_text, 'YourFile?')
01500         self.assertEqual(self.ui.report['answer'], '/etc/fstab')
01501 
01502         self.ui.question_file_response = None
01503         self.ui.run_report_bug()
01504         self.assertEqual(self.ui.report['answer'], 'None')
01505         self.assertEqual(self.ui.report['end'], '1')
01506 
01507     def test_interactive_hooks_choices(self):
01508         '''interactive hooks: HookUI.choice()'''
01509 
01510         self.ui.present_details_response = {'report': False,
01511                                             'blacklist': False,
01512                                             'examine': False,
01513                                             'restart': False}
01514         self.ui.question_choice_response = [1]
01515         self._run_hook('''report['begin'] = '1'
01516 report['answer'] = str(ui.choice('YourChoice?', ['foo', 'bar']))
01517 report['end'] = '1'
01518 ''')
01519         self.assertEqual(self.ui.report['begin'], '1')
01520         self.assertEqual(self.ui.report['end'], '1')
01521         self.assertEqual(self.ui.msg_text, 'YourChoice?')
01522         self.assertEqual(self.ui.report['answer'], '[1]')
01523 
01524         self.ui.question_choice_response = None
01525         self.ui.run_report_bug()
01526         self.assertEqual(self.ui.report['answer'], 'None')
01527         self.assertEqual(self.ui.report['end'], '1')
01528 
01529     def test_interactive_hooks_cancel(self):
01530         '''interactive hooks: user cancels'''
01531 
01532         self.assertRaises(SystemExit, self._run_hook,
01533                           '''report['begin'] = '1'
01534 raise StopIteration
01535 report['end'] = '1'
01536 ''')
01537 
01538     def test_run_symptom(self):
01539         '''run_symptom()'''
01540 
01541         # unknown symptom
01542         sys.argv = ['ui-test', '-s', 'foobar']
01543         self.ui = TestSuiteUserInterface()
01544         self.ui.present_details_response = {'report': True,
01545                                             'blacklist': False,
01546                                             'examine': False,
01547                                             'restart': False}
01548         self.assertEqual(self.ui.run_argv(), True)
01549         self.assertTrue('foobar" is not known' in self.ui.msg_text)
01550         self.assertEqual(self.ui.msg_severity, 'error')
01551 
01552         # does not determine package
01553         f = open(os.path.join(apport.ui.symptom_script_dir, 'nopkg.py'), 'w')
01554         f.write('def run(report, ui):\n    pass\n')
01555         f.close()
01556         orig_stderr = sys.stderr
01557         sys.argv = ['ui-test', '-s', 'nopkg']
01558         self.ui = TestSuiteUserInterface()
01559         sys.stderr = StringIO()
01560         self.assertRaises(SystemExit, self.ui.run_argv)
01561         err = sys.stderr.getvalue()
01562         sys.stderr = orig_stderr
01563         self.assertTrue('did not determine the affected package' in err)
01564 
01565         # does not define run()
01566         f = open(os.path.join(apport.ui.symptom_script_dir, 'norun.py'), 'w')
01567         f.write('def something(x, y):\n    return 1\n')
01568         f.close()
01569         sys.argv = ['ui-test', '-s', 'norun']
01570         self.ui = TestSuiteUserInterface()
01571         sys.stderr = StringIO()
01572         self.assertRaises(SystemExit, self.ui.run_argv)
01573         err = sys.stderr.getvalue()
01574         sys.stderr = orig_stderr
01575         self.assertTrue('norun.py crashed:' in err)
01576 
01577         # crashing script
01578         f = open(os.path.join(apport.ui.symptom_script_dir, 'crash.py'), 'w')
01579         f.write('def run(report, ui):\n    return 1/0\n')
01580         f.close()
01581         sys.argv = ['ui-test', '-s', 'crash']
01582         self.ui = TestSuiteUserInterface()
01583         sys.stderr = StringIO()
01584         self.assertRaises(SystemExit, self.ui.run_argv)
01585         err = sys.stderr.getvalue()
01586         sys.stderr = orig_stderr
01587         self.assertTrue('crash.py crashed:' in err)
01588         self.assertTrue('ZeroDivisionError:' in err)
01589 
01590         # working noninteractive script
01591         f = open(os.path.join(apport.ui.symptom_script_dir, 'itching.py'), 'w')
01592         f.write('def run(report, ui):\n  report["itch"] = "scratch"\n  return "bash"\n')
01593         f.close()
01594         sys.argv = ['ui-test', '-s', 'itching']
01595         self.ui = TestSuiteUserInterface()
01596         self.ui.present_details_response = {'report': True,
01597                                             'blacklist': False,
01598                                             'examine': False,
01599                                             'restart': False}
01600         self.assertEqual(self.ui.run_argv(), True)
01601         self.assertEqual(self.ui.msg_text, None)
01602         self.assertEqual(self.ui.msg_severity, None)
01603         self.assertTrue(self.ui.present_details_shown)
01604 
01605         self.assertEqual(self.ui.report['itch'], 'scratch')
01606         self.assertTrue('DistroRelease' in self.ui.report)
01607         self.assertEqual(self.ui.report['SourcePackage'], 'bash')
01608         self.assertTrue(self.ui.report['Package'].startswith('bash '))
01609         self.assertEqual(self.ui.report['ProblemType'], 'Bug')
01610 
01611         # working noninteractive script with extra tag
01612         sys.argv = ['ui-test', '--tag', 'foo', '-s', 'itching']
01613         self.ui = TestSuiteUserInterface()
01614         self.ui.present_details_response = {'report': True,
01615                                             'blacklist': False,
01616                                             'examine': False,
01617                                             'restart': False}
01618         self.assertEqual(self.ui.run_argv(), True)
01619         self.assertEqual(self.ui.msg_text, None)
01620         self.assertEqual(self.ui.msg_severity, None)
01621         self.assertTrue(self.ui.present_details_shown)
01622 
01623         self.assertEqual(self.ui.report['itch'], 'scratch')
01624         self.assertTrue('foo' in self.ui.report['Tags'])
01625 
01626         # working interactive script
01627         f = open(os.path.join(apport.ui.symptom_script_dir, 'itching.py'), 'w')
01628         f.write('''def run(report, ui):
01629     report['itch'] = 'slap'
01630     report['q'] = str(ui.yesno('do you?'))
01631     return 'bash'
01632 ''')
01633         f.close()
01634         sys.argv = ['ui-test', '-s', 'itching']
01635         self.ui = TestSuiteUserInterface()
01636         self.ui.present_details_response = {'report': True,
01637                                             'blacklist': False,
01638                                             'examine': False,
01639                                             'restart': False}
01640         self.ui.question_yesno_response = True
01641         self.assertEqual(self.ui.run_argv(), True)
01642         self.assertTrue(self.ui.present_details_shown)
01643         self.assertEqual(self.ui.msg_text, 'do you?')
01644 
01645         self.assertEqual(self.ui.report['itch'], 'slap')
01646         self.assertTrue('DistroRelease' in self.ui.report)
01647         self.assertEqual(self.ui.report['SourcePackage'], 'bash')
01648         self.assertTrue(self.ui.report['Package'].startswith('bash '))
01649         self.assertEqual(self.ui.report['ProblemType'], 'Bug')
01650         self.assertEqual(self.ui.report['q'], 'True')
01651 
01652     def test_run_report_bug_list_symptoms(self):
01653         '''run_report_bug() without specifying arguments and available symptoms'''
01654 
01655         f = open(os.path.join(apport.ui.symptom_script_dir, 'foo.py'), 'w')
01656         f.write('''description = 'foo does not work'
01657 def run(report, ui):
01658     return 'bash'
01659 ''')
01660         f.close()
01661         f = open(os.path.join(apport.ui.symptom_script_dir, 'bar.py'), 'w')
01662         f.write('def run(report, ui):\n  return "coreutils"\n')
01663         f.close()
01664 
01665         sys.argv = ['ui-test', '-f']
01666         self.ui = TestSuiteUserInterface()
01667         self.ui.present_details_response = {'report': True,
01668                                             'blacklist': False,
01669                                             'examine': False,
01670                                             'restart': False}
01671 
01672         self.ui.question_choice_response = None
01673         self.assertEqual(self.ui.run_argv(), True)
01674         self.assertEqual(self.ui.msg_severity, None)
01675         self.assertTrue('kind of problem' in self.ui.msg_text)
01676         self.assertEqual(set(self.ui.msg_choices),
01677                          set(['bar', 'foo does not work', 'Other problem']))
01678 
01679         # cancelled
01680         self.assertEqual(self.ui.ic_progress_pulses, 0)
01681         self.assertEqual(self.ui.report, None)
01682         self.assertFalse(self.ui.present_details_shown)
01683 
01684         # now, choose foo -> bash report
01685         self.ui.question_choice_response = [self.ui.msg_choices.index('foo does not work')]
01686         self.assertEqual(self.ui.run_argv(), True)
01687         self.assertEqual(self.ui.msg_severity, None)
01688         self.assertTrue(self.ui.ic_progress_pulses > 0)
01689         self.assertTrue(self.ui.present_details_shown)
01690         self.assertTrue(self.ui.report['Package'].startswith('bash'))
01691 
01692     def test_parse_argv_single_arg(self):
01693         '''parse_args() option inference for a single argument'''
01694 
01695         def _chk(program_name, arg, expected_opts):
01696             sys.argv = [program_name]
01697             if arg:
01698                 sys.argv.append(arg)
01699             orig_stderr = sys.stderr
01700             sys.stderr = open('/dev/null', 'w')
01701             try:
01702                 ui = apport.ui.UserInterface()
01703             finally:
01704                 sys.stderr.close()
01705                 sys.stderr = orig_stderr
01706             expected_opts['version'] = None
01707             self.assertEqual(ui.args, [])
01708             self.assertEqual(ui.options, expected_opts)
01709 
01710         # no arguments -> show pending crashes
01711         _chk('apport-gtk', None,
01712              {'filebug': False, 'package': None, 'pid': None, 'crash_file':
01713               None, 'symptom': None, 'update_report': None, 'save': None,
01714               'window': False, 'tag': [], 'hanging': False})
01715         # updating report not allowed without args
01716         self.assertRaises(SystemExit, _chk, 'apport-collect', None, {})
01717 
01718         # package
01719         _chk('apport-kde', 'coreutils',
01720              {'filebug': True, 'package': 'coreutils', 'pid': None,
01721               'crash_file': None, 'symptom': None, 'update_report': None,
01722               'save': None, 'window': False, 'tag': [], 'hanging': False})
01723 
01724         # symptom is preferred over package
01725         f = open(os.path.join(apport.ui.symptom_script_dir, 'coreutils.py'), 'w')
01726         f.write('''description = 'foo does not work'
01727 def run(report, ui):
01728 return 'bash'
01729 ''')
01730         f.close()
01731         _chk('apport-cli', 'coreutils',
01732              {'filebug': True, 'package': None, 'pid': None, 'crash_file':
01733               None, 'symptom': 'coreutils', 'update_report': None, 'save':
01734               None, 'window': False, 'tag': [], 'hanging': False})
01735 
01736         # PID
01737         _chk('apport-cli', '1234', {'filebug': True, 'package': None,
01738              'pid': '1234', 'crash_file': None, 'symptom': None,
01739              'update_report': None, 'save': None, 'window': False,
01740              'tag': [], 'hanging': False})
01741 
01742         # .crash/.apport files; check correct handling of spaces
01743         for suffix in ('.crash', '.apport'):
01744             _chk('apport-cli', '/tmp/f oo' + suffix, {'filebug': False,
01745                  'package': None, 'pid': None,
01746                  'crash_file': '/tmp/f oo' + suffix, 'symptom': None,
01747                  'update_report': None, 'save': None, 'window': False,
01748                  'tag': [], 'hanging': False})
01749 
01750         # executable
01751         _chk('apport-cli', '/usr/bin/tail', {'filebug': True,
01752              'package': 'coreutils',
01753              'pid': None, 'crash_file': None, 'symptom': None,
01754              'update_report': None, 'save': None, 'window': False,
01755              'tag': [], 'hanging': False})
01756 
01757         # update existing report
01758         _chk('apport-collect', '1234', {'filebug': False, 'package': None,
01759              'crash_file': None, 'symptom': None, 'update_report': 1234,
01760              'tag': []})
01761         _chk('apport-update-bug', '1234', {'filebug': False, 'package': None,
01762              'crash_file': None, 'symptom': None, 'update_report': 1234,
01763              'tag': []})
01764 
01765     def test_parse_argv_apport_bug(self):
01766         '''parse_args() option inference when invoked as *-bug'''
01767 
01768         def _chk(args, expected_opts):
01769             sys.argv = ['apport-bug'] + args
01770             orig_stderr = sys.stderr
01771             sys.stderr = open('/dev/null', 'w')
01772             try:
01773                 ui = apport.ui.UserInterface()
01774             finally:
01775                 sys.stderr.close()
01776                 sys.stderr = orig_stderr
01777             expected_opts['version'] = None
01778             self.assertEqual(ui.args, [])
01779             self.assertEqual(ui.options, expected_opts)
01780 
01781         #
01782         # no arguments: default to 'ask for symptom' bug mode
01783         #
01784         _chk([], {'filebug': True, 'package': None, 'pid': None, 'crash_file':
01785                   None, 'symptom': None, 'update_report': None, 'save': None,
01786                   'window': False, 'tag': [], 'hanging': False})
01787 
01788         #
01789         # single arguments
01790         #
01791 
01792         # package
01793         _chk(['coreutils'], {'filebug': True, 'package': 'coreutils', 'pid':
01794                              None, 'crash_file': None, 'symptom': None,
01795                              'update_report': None, 'save': None, 'window':
01796                              False, 'tag': [], 'hanging': False})
01797 
01798         # symptom (preferred over package)
01799         f = open(os.path.join(apport.ui.symptom_script_dir, 'coreutils.py'), 'w')
01800         f.write('''description = 'foo does not work'
01801 def run(report, ui):
01802 return 'bash'
01803 ''')
01804         f.close()
01805         _chk(['coreutils'], {'filebug': True, 'package': None, 'pid': None,
01806                              'crash_file': None, 'symptom': 'coreutils',
01807                              'update_report': None, 'save': None, 'window':
01808                              False, 'tag': [], 'hanging': False})
01809         os.unlink(os.path.join(apport.ui.symptom_script_dir, 'coreutils.py'))
01810 
01811         # PID
01812         _chk(['1234'], {'filebug': True, 'package': None, 'pid': '1234',
01813                         'crash_file': None, 'symptom': None, 'update_report':
01814                         None, 'save': None, 'window': False, 'tag': [],
01815                         'hanging': False})
01816 
01817         # .crash/.apport files; check correct handling of spaces
01818         for suffix in ('.crash', '.apport'):
01819             _chk(['/tmp/f oo' + suffix],
01820                  {'filebug': False, 'package': None, 'pid': None, 'crash_file':
01821                   '/tmp/f oo' + suffix, 'symptom': None, 'update_report': None,
01822                   'save': None, 'window': False, 'tag': [], 'hanging': False})
01823 
01824         # executable name
01825         _chk(['/usr/bin/tail'],
01826              {'filebug': True, 'package': 'coreutils', 'pid': None,
01827               'crash_file': None, 'symptom': None, 'update_report': None,
01828               'save': None, 'window': False, 'tag': [], 'hanging': False})
01829 
01830         #
01831         # supported options
01832         #
01833 
01834         # --save
01835         _chk(['--save', 'foo.apport', 'coreutils'],
01836              {'filebug': True, 'package': 'coreutils', 'pid': None,
01837               'crash_file': None, 'symptom': None, 'update_report': None,
01838               'save': 'foo.apport', 'window': False, 'tag': [],
01839               'hanging': False})
01840 
01841         # --tag
01842         _chk(['--tag', 'foo', 'coreutils'],
01843              {'filebug': True, 'package': 'coreutils', 'pid': None,
01844               'crash_file': None, 'symptom': None, 'update_report': None,
01845               'save': None, 'window': False, 'tag': ['foo'], 'hanging': False})
01846         _chk(['--tag', 'foo', '--tag', 'bar', 'coreutils'],
01847              {'filebug': True, 'package': 'coreutils', 'pid': None,
01848               'crash_file': None, 'symptom': None, 'update_report': None,
01849               'save': None, 'window': False, 'tag': ['foo', 'bar'],
01850               'hanging': False})
01851 
01852     def test_can_examine_locally_crash(self):
01853         '''can_examine_locally() for a crash report'''
01854 
01855         self.ui.load_report(self.report_file.name)
01856 
01857         orig_path = os.environ['PATH']
01858         orig_fn = self.ui.ui_run_terminal
01859         try:
01860             self.ui.ui_run_terminal = lambda command: True
01861             os.environ['PATH'] = ''
01862             self.assertEqual(self.ui.can_examine_locally(), False)
01863 
01864             src_bindir = os.path.join(
01865                 os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'bin')
01866             # this will only work for running the tests in the source tree
01867             if os.access(os.path.join(src_bindir, 'apport-retrace'), os.X_OK):
01868                 os.environ['PATH'] = src_bindir
01869                 self.assertEqual(self.ui.can_examine_locally(), True)
01870             else:
01871                 # if we run tests in installed system, we just check that
01872                 # it doesn't crash
01873                 self.assertTrue(self.ui.can_examine_locally() in [False, True])
01874 
01875             self.ui.ui_run_terminal = lambda command: False
01876             self.assertEqual(self.ui.can_examine_locally(), False)
01877 
01878             # does not crash on NotImplementedError
01879             self.ui.ui_run_terminal = orig_fn
01880             self.assertEqual(self.ui.can_examine_locally(), False)
01881 
01882         finally:
01883             os.environ['PATH'] = orig_path
01884             self.ui.ui_run_terminal = orig_fn
01885 
01886     def test_can_examine_locally_nocrash(self):
01887         '''can_examine_locally() for a non-crash report'''
01888 
01889         self.ui.load_report(self.report_file.name)
01890         del self.ui.report['CoreDump']
01891 
01892         orig_fn = self.ui.ui_run_terminal
01893         try:
01894             self.ui.ui_run_terminal = lambda command: True
01895             self.assertEqual(self.ui.can_examine_locally(), False)
01896         finally:
01897             self.ui.ui_run_terminal = orig_fn
01898 
01899     def test_db_no_accept(self):
01900         '''crash database does not accept report'''
01901 
01902         # FIXME: This behaviour is not really correct, but necessary as long as
01903         # we only support a single crashdb and have whoopsie hardcoded
01904         # (see LP#957177)
01905 
01906         latest_id_before = self.ui.crashdb.latest_id()
01907 
01908         sys.argv = ['ui-test', '-f', '-p', 'bash']
01909         self.ui = TestSuiteUserInterface()
01910 
01911         # Pretend it does not accept report
01912         self.ui.crashdb.accepts = lambda r: False
01913         self.ui.present_details_response = {'report': True,
01914                                             'blacklist': False,
01915                                             'examine': False,
01916                                             'restart': False}
01917         self.assertEqual(self.ui.run_argv(), True)
01918 
01919         self.assertEqual(self.ui.msg_severity, None)
01920         self.assertEqual(self.ui.msg_title, None)
01921         self.assertTrue(self.ui.present_details_shown)
01922 
01923         # data was collected for whoopsie
01924         self.assertEqual(self.ui.report['SourcePackage'], 'bash')
01925         self.assertTrue('Dependencies' in self.ui.report.keys())
01926         self.assertTrue('ProcEnviron' in self.ui.report.keys())
01927         self.assertEqual(self.ui.report['ProblemType'], 'Bug')
01928 
01929         # no upload happend
01930         self.assertEqual(self.ui.opened_url, None)
01931         self.assertEqual(self.ui.upload_progress_pulses, 0)
01932         self.assertEqual(self.ui.crashdb.latest_id(), latest_id_before)
01933 
01934     def test_get_desktop_entry(self):
01935         desktop_file = tempfile.NamedTemporaryFile()
01936         desktop_file.write('''[Desktop Entry]
01937 Name=gtranslate
01938 GenericName=Translator
01939 GenericName[de]=√úbersetzer
01940 Exec=gedit %U
01941 Categories=GNOME;GTK;Utility;TextEditor;
01942 '''.encode('UTF-8'))
01943         desktop_file.flush()
01944 
01945         self.report['DesktopFile'] = desktop_file.name
01946         self.ui.report = self.report
01947         info = self.ui.get_desktop_entry()
01948         self.assertEqual(info, {'genericname': 'Translator',
01949                                 'categories': 'GNOME;GTK;Utility;TextEditor;',
01950                                 'name': 'gtranslate',
01951                                 'genericname[de]': '√úbersetzer',
01952                                 'exec': 'gedit %U'})
01953 
01954     def test_wait_for_pid(self):
01955         # fork a test process
01956         pid = os.fork()
01957         if pid == 0:
01958             os.dup2(os.open('/dev/null', os.O_WRONLY), sys.stdout.fileno())
01959             os.execv('/usr/bin/yes', ['yes'])
01960             assert False, 'Could not execute /usr/bin/yes'
01961 
01962         time.sleep(0.5)
01963         os.kill(pid, signal.SIGKILL)
01964         os.waitpid(pid, 0)
01965         self.ui.wait_for_pid(pid)
01966 
01967 
01968 unittest.main()