Back to index

apport  2.3
test_ui_gtk.py
Go to the documentation of this file.
00001 # coding: UTF-8
00002 '''GTK Apport user interface tests.'''
00003 
00004 # Copyright (C) 2012 Canonical Ltd.
00005 # Author: Evan Dandrea <evan.dandrea@canonical.com>
00006 #
00007 # This program is free software; you can redistribute it and/or modify it
00008 # under the terms of the GNU General Public License as published by the
00009 # Free Software Foundation; either version 2 of the License, or (at your
00010 # option) any later version.  See http://www.gnu.org/copyleft/gpl.html for
00011 # the full text of the license.
00012 
00013 import unittest
00014 import tempfile
00015 import sys
00016 import os
00017 import imp
00018 import apport
00019 import shutil
00020 import subprocess
00021 from gi.repository import GLib, Gtk
00022 from apport import unicode_gettext as _
00023 from mock import patch
00024 
00025 import apport.crashdb_impl.memory
00026 
00027 GLib.log_set_always_fatal(GLib.LogLevelFlags.LEVEL_WARNING | GLib.LogLevelFlags.LEVEL_CRITICAL)
00028 
00029 if os.environ.get('APPORT_TEST_LOCAL'):
00030     apport_gtk_path = 'gtk/apport-gtk'
00031     kernel_oops_path = 'data/kernel_oops'
00032 else:
00033     apport_gtk_path = os.path.join(os.environ.get('APPORT_DATA_DIR', '/usr/share/apport'), 'apport-gtk')
00034     kernel_oops_path = os.path.join(os.environ.get('APPORT_DATA_DIR', '/usr/share/apport'), 'kernel_oops')
00035 GTKUserInterface = imp.load_source('', apport_gtk_path).GTKUserInterface
00036 
00037 
00038 class T(unittest.TestCase):
00039     @classmethod
00040     def setUpClass(klass):
00041         r = apport.Report()
00042         r.add_os_info()
00043         klass.distro = r['DistroRelease'].split()[0]
00044 
00045     def setUp(self):
00046         self.report_dir = tempfile.mkdtemp()
00047         apport.fileutils.report_dir = self.report_dir
00048         os.environ['APPORT_REPORT_DIR'] = self.report_dir
00049         # do not cause eternal hangs because of error dialog boxes
00050         os.environ['APPORT_DISABLE_DISTRO_CHECK'] = '1'
00051 
00052         saved = sys.argv[0]
00053         # Work around GTKUserInterface using basename to find the GtkBuilder UI
00054         # file.
00055         sys.argv[0] = apport_gtk_path
00056         self.app = GTKUserInterface()
00057         sys.argv[0] = saved
00058 
00059         # use in-memory crashdb
00060         self.app.crashdb = apport.crashdb_impl.memory.CrashDatabase(None, {})
00061 
00062         # test report
00063         self.app.report_file = os.path.join(self.report_dir, 'bash.crash')
00064 
00065         self.app.report = apport.Report()
00066         self.app.report['ExecutablePath'] = '/bin/bash'
00067         self.app.report['Signal'] = '11'
00068         self.app.report['CoreDump'] = b'\x01\x02'
00069         self.app.report['DistroRelease'] = self.distro
00070         with open(self.app.report_file, 'wb') as f:
00071             self.app.report.write(f)
00072 
00073         # disable package hooks, as they might ask for sudo password and other
00074         # interactive bits; allow tests to install their own hooks
00075         self.hook_dir = tempfile.mkdtemp()
00076         apport.report._hook_dir = self.hook_dir
00077         apport.report._common_hook_dir = self.hook_dir
00078 
00079     def tearDown(self):
00080         shutil.rmtree(self.report_dir)
00081         shutil.rmtree(self.hook_dir)
00082 
00083     def test_close_button(self):
00084         '''Clicking the close button on the window does not report the crash.'''
00085 
00086         def c(*args):
00087             self.app.w('dialog_crash_new').destroy()
00088         self.app.w('send_error_report').set_active(True)
00089         GLib.idle_add(c)
00090         result = self.app.ui_present_report_details(True)
00091         self.assertFalse(result['report'])
00092 
00093     def test_kernel_crash_layout(self):
00094         '''
00095         +-----------------------------------------------------------------+
00096         | [ logo] YourDistro has experienced an internal error.           |
00097         |                                                                 |
00098         |            [x] Send an error report to help fix this problem.   |
00099         |                                                                 |
00100         | [ Show Details ]                                   [ Continue ] |
00101         +-----------------------------------------------------------------+
00102         '''
00103         self.app.report['ProblemType'] = 'KernelCrash'
00104         GLib.idle_add(Gtk.main_quit)
00105         self.app.ui_present_report_details(True)
00106         self.assertEqual(self.app.w('dialog_crash_new').get_title(), self.distro)
00107         self.assertEqual(self.app.w('title_label').get_text(),
00108                          _('Sorry, %s has experienced an internal error.') % self.distro)
00109         send_error_report = self.app.w('send_error_report')
00110         self.assertTrue(send_error_report.get_property('visible'))
00111         self.assertTrue(send_error_report.get_active())
00112         self.assertTrue(self.app.w('show_details').get_property('visible'))
00113         self.assertTrue(self.app.w('continue_button').get_property('visible'))
00114         self.assertEqual(self.app.w('continue_button').get_label(),
00115                          _('Continue'))
00116         self.assertFalse(self.app.w('closed_button').get_property('visible'))
00117         self.assertFalse(self.app.w('subtitle_label').get_property('visible'))
00118         self.assertFalse(self.app.w('ignore_future_problems').get_property('visible'))
00119 
00120     def test_package_crash_layout(self):
00121         '''
00122         +-----------------------------------------------------------------+
00123         | [ error  ] Sorry, a problem occurred while installing software. |
00124         |            Package: apport 1.2.3~0ubuntu1                       |
00125         |                                                                 |
00126         |            [x] Send an error report to help fix this problem.   |
00127         |                                                                 |
00128         | [ Show Details ]                                   [ Continue ] |
00129         +-----------------------------------------------------------------+
00130         '''
00131         self.app.report['ProblemType'] = 'Package'
00132         self.app.report['Package'] = 'apport 1.2.3~0ubuntu1'
00133         GLib.idle_add(Gtk.main_quit)
00134         self.app.ui_present_report_details(True)
00135         self.assertEqual(self.app.w('dialog_crash_new').get_title(), self.distro)
00136         self.assertEqual(self.app.w('title_label').get_text(),
00137                          _('Sorry, a problem occurred while installing software.'))
00138         send_error_report = self.app.w('send_error_report')
00139         self.assertTrue(send_error_report.get_property('visible'))
00140         self.assertTrue(send_error_report.get_active())
00141         self.assertTrue(self.app.w('show_details').get_property('visible'))
00142         self.assertTrue(self.app.w('continue_button').get_property('visible'))
00143         self.assertEqual(self.app.w('continue_button').get_label(),
00144                          _('Continue'))
00145         self.assertFalse(self.app.w('closed_button').get_property('visible'))
00146         self.assertTrue(self.app.w('subtitle_label').get_property('visible'))
00147         self.assertEqual(self.app.w('subtitle_label').get_text(),
00148                          _('Package: apport 1.2.3~0ubuntu1'))
00149 
00150     def test_regular_crash_layout(self):
00151         '''
00152         +-----------------------------------------------------------------+
00153         | [ apport ] The application Apport has closed unexpectedly.      |
00154         |                                                                 |
00155         |            [x] Send an error report to help fix this problem.   |
00156         |            [ ] Ignore future problems of this program version.  |
00157         |                                                                 |
00158         | [ Show Details ]                                   [ Continue ] |
00159         +-----------------------------------------------------------------+
00160         '''
00161         self.app.report['ProblemType'] = 'Crash'
00162         self.app.report['CrashCounter'] = '1'
00163         self.app.report['Package'] = 'apport 1.2.3~0ubuntu1'
00164         with tempfile.NamedTemporaryFile() as fp:
00165             fp.write(b'''[Desktop Entry]
00166 Version=1.0
00167 Name=Apport
00168 Type=Application''')
00169             fp.flush()
00170             self.app.report['DesktopFile'] = fp.name
00171             GLib.idle_add(Gtk.main_quit)
00172             self.app.ui_present_report_details(True)
00173         self.assertEqual(self.app.w('dialog_crash_new').get_title(), self.distro)
00174         self.assertEqual(self.app.w('title_label').get_text(),
00175                          _('The application Apport has closed unexpectedly.'))
00176         send_error_report = self.app.w('send_error_report')
00177         self.assertTrue(send_error_report.get_property('visible'))
00178         self.assertTrue(send_error_report.get_active())
00179         self.assertTrue(self.app.w('show_details').get_property('visible'))
00180         # no ProcCmdline, cannot restart
00181         self.assertTrue(self.app.w('continue_button').get_property('visible'))
00182         self.assertEqual(self.app.w('continue_button').get_label(),
00183                          _('Continue'))
00184         self.assertFalse(self.app.w('closed_button').get_property('visible'))
00185         self.assertFalse(self.app.w('subtitle_label').get_property('visible'))
00186         self.assertTrue(self.app.w('ignore_future_problems').get_property('visible'))
00187         self.assertTrue(self.app.w('ignore_future_problems').get_label().endswith(
00188             'of this program version'))
00189 
00190     def test_regular_crash_layout_restart(self):
00191         '''
00192         +-----------------------------------------------------------------+
00193         | [ apport ] The application Apport has closed unexpectedly.      |
00194         |                                                                 |
00195         |            [x] Send an error report to help fix this problem.   |
00196         |            [ ] Ignore future problems of this program version.  |
00197         |                                                                 |
00198         | [ Show Details ]                 [ Leave Closed ]  [ Relaunch ] |
00199         +-----------------------------------------------------------------+
00200         '''
00201         self.app.report['ProblemType'] = 'Crash'
00202         self.app.report['CrashCounter'] = '1'
00203         self.app.report['ProcCmdline'] = 'apport-bug apport'
00204         self.app.report['Package'] = 'apport 1.2.3~0ubuntu1'
00205         with tempfile.NamedTemporaryFile() as fp:
00206             fp.write(b'''[Desktop Entry]
00207 Version=1.0
00208 Name=Apport
00209 Type=Application''')
00210             fp.flush()
00211             self.app.report['DesktopFile'] = fp.name
00212             GLib.idle_add(Gtk.main_quit)
00213             self.app.ui_present_report_details(True)
00214         self.assertEqual(self.app.w('dialog_crash_new').get_title(), self.distro)
00215         self.assertEqual(self.app.w('title_label').get_text(),
00216                          _('The application Apport has closed unexpectedly.'))
00217         send_error_report = self.app.w('send_error_report')
00218         self.assertTrue(send_error_report.get_property('visible'))
00219         self.assertTrue(send_error_report.get_active())
00220         self.assertTrue(self.app.w('show_details').get_property('visible'))
00221         self.assertTrue(self.app.w('continue_button').get_property('visible'))
00222         self.assertEqual(self.app.w('continue_button').get_label(),
00223                          _('Relaunch'))
00224         self.assertTrue(self.app.w('closed_button').get_property('visible'))
00225         self.assertFalse(self.app.w('subtitle_label').get_property('visible'))
00226         self.assertTrue(self.app.w('ignore_future_problems').get_property('visible'))
00227         self.assertTrue(self.app.w('ignore_future_problems').get_label().endswith(
00228             'of this program version'))
00229 
00230     def test_hang_layout(self):
00231         '''
00232         +-----------------------------------------------------------------+
00233         | [ apport ] The application Apport has stopped responding.       |
00234         |                                                                 |
00235         |            [x] Send an error report to help fix this problem.   |
00236         |                                                                 |
00237         | [ Show Details ]                 [ Force Closed ]  [ Relaunch ] |
00238         +-----------------------------------------------------------------+
00239         '''
00240         self.app.report['ProblemType'] = 'Hang'
00241         self.app.report['ProcCmdline'] = 'apport-bug apport'
00242         self.app.report['Package'] = 'apport 1.2.3~0ubuntu1'
00243         with tempfile.NamedTemporaryFile() as fp:
00244             fp.write(b'''[Desktop Entry]
00245 Version=1.0
00246 Name=Apport
00247 Type=Application''')
00248             fp.flush()
00249             self.app.report['DesktopFile'] = fp.name
00250             GLib.idle_add(Gtk.main_quit)
00251             self.app.ui_present_report_details(True)
00252         self.assertEqual(self.app.w('dialog_crash_new').get_title(), self.distro)
00253         self.assertEqual(self.app.w('title_label').get_text(),
00254                          _('The application Apport has stopped responding.'))
00255         send_error_report = self.app.w('send_error_report')
00256         self.assertTrue(send_error_report.get_property('visible'))
00257         self.assertTrue(send_error_report.get_active())
00258         self.assertTrue(self.app.w('show_details').get_property('visible'))
00259         self.assertTrue(self.app.w('continue_button').get_property('visible'))
00260         self.assertEqual(self.app.w('continue_button').get_label(),
00261                          _('Relaunch'))
00262         self.assertTrue(self.app.w('closed_button').get_property('visible'))
00263         self.assertEqual(self.app.w('closed_button').get_label(),
00264                          _('Force Closed'))
00265         self.assertFalse(self.app.w('subtitle_label').get_property('visible'))
00266         self.assertFalse(self.app.w('ignore_future_problems').get_property('visible'))
00267 
00268     def test_system_crash_layout(self):
00269         '''
00270         +---------------------------------------------------------------+
00271         | [ logo ] Sorry, YourDistro has experienced an internal error. |
00272         |          If you notice further problems, try restarting the   |
00273         |          computer                                             |
00274         |                                                               |
00275         |            [x] Send an error report to help fix this problem. |
00276         |            [ ] Ignore future problems of this type.           |
00277         |                                                               |
00278         | [ Show Details ]                                 [ Continue ] |
00279         +---------------------------------------------------------------+
00280         '''
00281         self.app.report['ProblemType'] = 'Crash'
00282         self.app.report['CrashCounter'] = '1'
00283         self.app.report['Package'] = 'bash 5'
00284         GLib.idle_add(Gtk.main_quit)
00285         self.app.ui_present_report_details(True)
00286         self.assertEqual(self.app.w('dialog_crash_new').get_title(), self.distro)
00287         self.assertEqual(self.app.w('title_label').get_text(),
00288                          _('Sorry, %s has experienced an internal error.') % self.distro)
00289         self.assertEqual(self.app.w('subtitle_label').get_text(),
00290                          _('If you notice further problems, try restarting the computer.'))
00291         self.assertTrue(self.app.w('subtitle_label').get_property('visible'))
00292         send_error_report = self.app.w('send_error_report')
00293         self.assertTrue(send_error_report.get_property('visible'))
00294         self.assertTrue(send_error_report.get_active())
00295         self.assertTrue(self.app.w('show_details').get_property('visible'))
00296         self.assertTrue(self.app.w('continue_button').get_property('visible'))
00297         self.assertEqual(self.app.w('continue_button').get_label(),
00298                          _('Continue'))
00299         self.assertFalse(self.app.w('closed_button').get_property('visible'))
00300         self.assertTrue(self.app.w('ignore_future_problems').get_property('visible'))
00301         self.assertTrue(self.app.w('ignore_future_problems').get_label().endswith(
00302             'of this type'))
00303 
00304     def test_system_crash_from_console_layout(self):
00305         '''
00306         +-------------------------------------------------------------------+
00307         | [ ubuntu ] Sorry, the application apport has closed unexpectedly. |
00308         |            If you notice further problems, try restarting the     |
00309         |            computer                                               |
00310         |                                                                   |
00311         |            [x] Send an error report to help fix this problem.     |
00312         |                                                                   |
00313         | [ Show Details ]                                     [ Continue ] |
00314         +-------------------------------------------------------------------+
00315         '''
00316         self.app.report['ProblemType'] = 'Crash'
00317         self.app.report['Package'] = 'bash 5'
00318         self.app.report['ProcEnviron'] = ('LANGUAGE=en_GB:en\n'
00319                                           'SHELL=/bin/sh\n'
00320                                           'TERM=xterm')
00321         self.app.report['ExecutablePath'] = '/usr/bin/apport'
00322         # This will be set by apport/ui.py in load_report()
00323         self.app.cur_package = 'apport'
00324         GLib.idle_add(Gtk.main_quit)
00325         self.app.ui_present_report_details(True)
00326         self.assertEqual(self.app.w('dialog_crash_new').get_title(), self.distro)
00327         self.assertEqual(self.app.w('title_label').get_text(),
00328                          _('Sorry, the application apport has closed unexpectedly.'))
00329         self.assertEqual(self.app.w('subtitle_label').get_text(),
00330                          _('If you notice further problems, try restarting the computer.'))
00331         self.assertTrue(self.app.w('subtitle_label').get_property('visible'))
00332         send_error_report = self.app.w('send_error_report')
00333         self.assertTrue(send_error_report.get_property('visible'))
00334         self.assertTrue(send_error_report.get_active())
00335         self.assertTrue(self.app.w('show_details').get_property('visible'))
00336         self.assertTrue(self.app.w('continue_button').get_property('visible'))
00337         self.assertEqual(self.app.w('continue_button').get_label(),
00338                          _('Continue'))
00339         self.assertFalse(self.app.w('closed_button').get_property('visible'))
00340 
00341         del self.app.report['ExecutablePath']
00342         GLib.idle_add(Gtk.main_quit)
00343         self.app.ui_present_report_details(True)
00344         self.assertEqual(self.app.w('title_label').get_text(),
00345                          _('Sorry, apport has closed unexpectedly.'))
00346 
00347         # no crash counter
00348         self.assertFalse(self.app.w('ignore_future_problems').get_property('visible'))
00349 
00350     @patch.object(GTKUserInterface, 'can_examine_locally')
00351     def test_examine_button(self, *args):
00352         '''
00353         +---------------------------------------------------------------------+
00354         | [ apport ] The application Apport has closed unexpectedly.          |
00355         |                                                                     |
00356         |            [x] Send an error report to help fix this problem.       |
00357         |                                                                     |
00358         | [ Show Details ] [ Examine locally ]  [ Leave Closed ] [ Relaunch ] |
00359         +---------------------------------------------------------------------+
00360         '''
00361         self.app.report['ProblemType'] = 'Crash'
00362         self.app.report['Package'] = 'bash 5'
00363 
00364         GLib.idle_add(Gtk.main_quit)
00365         self.app.can_examine_locally.return_value = False
00366         self.app.ui_present_report_details(True)
00367         self.assertFalse(self.app.w('examine').get_property('visible'))
00368 
00369         GLib.idle_add(self.app.w('examine').clicked)
00370         self.app.can_examine_locally.return_value = True
00371         result = self.app.ui_present_report_details(True)
00372         self.assertTrue(self.app.w('examine').get_property('visible'))
00373         self.assertTrue(result['examine'])
00374 
00375     def test_apport_bug_package_layout(self):
00376         '''
00377         +-------------------------------------------------------------------+
00378         | [ error  ] Send problem report to the developers?                 |
00379         |                                                                   |
00380         |            +----------------------------------------------------+ |
00381         |            | |> ApportVersion                                   | |
00382         |            | ...                                                | |
00383         |            +----------------------------------------------------+ |
00384         |                                                                   |
00385         | [ Cancel ]                                               [ Send ] |
00386         +-------------------------------------------------------------------+
00387         '''
00388         self.app.report_file = None
00389         GLib.idle_add(Gtk.main_quit)
00390         self.app.ui_present_report_details(True)
00391         self.assertEqual(self.app.w('title_label').get_text(),
00392                          _('Send problem report to the developers?'))
00393         self.assertFalse(self.app.w('subtitle_label').get_property('visible'))
00394         send_error_report = self.app.w('send_error_report')
00395         self.assertFalse(send_error_report.get_property('visible'))
00396         self.assertTrue(send_error_report.get_active())
00397         self.assertFalse(self.app.w('show_details').get_property('visible'))
00398         self.assertTrue(self.app.w('continue_button').get_property('visible'))
00399         self.assertEqual(self.app.w('continue_button').get_label(),
00400                          _('Send'))
00401         self.assertFalse(self.app.w('closed_button').get_property('visible'))
00402         self.assertTrue(self.app.w('cancel_button').get_property('visible'))
00403         self.assertTrue(self.app.w('details_scrolledwindow').get_property('visible'))
00404         self.assertTrue(self.app.w('dialog_crash_new').get_resizable())
00405 
00406     def test_administrator_disabled_reporting(self):
00407         GLib.idle_add(Gtk.main_quit)
00408         self.app.ui_present_report_details(False)
00409         send_error_report = self.app.w('send_error_report')
00410         self.assertFalse(send_error_report.get_property('visible'))
00411         self.assertFalse(send_error_report.get_active())
00412 
00413     @patch.object(GTKUserInterface, 'open_url')
00414     @patch.object(GTKUserInterface, 'ui_start_upload_progress')
00415     @patch.object(GTKUserInterface, 'ui_stop_upload_progress')
00416     def test_crash_nodetails(self, *args):
00417         '''Crash report without showing details'''
00418 
00419         self.visible_progress = None
00420 
00421         def cont(*args):
00422             if not self.app.w('continue_button').get_visible():
00423                 return True
00424             self.app.w('continue_button').clicked()
00425             GLib.timeout_add(500, check_progress)
00426             return False
00427 
00428         def check_progress(*args):
00429             self.visible_progress = self.app.w(
00430                 'window_information_collection').get_property('visible')
00431             return False
00432 
00433         GLib.timeout_add_seconds(1, cont)
00434         self.app.run_crash(self.app.report_file)
00435 
00436         # we should have reported one crash
00437         self.assertEqual(self.app.crashdb.latest_id(), 0)
00438         r = self.app.crashdb.download(0)
00439         self.assertEqual(r['ProblemType'], 'Crash')
00440         self.assertEqual(r['ExecutablePath'], '/bin/bash')
00441 
00442         # should show a progress bar for info collection
00443         self.assertEqual(self.visible_progress, True)
00444 
00445         # data was collected
00446         self.assertTrue(r['Package'].startswith('bash '))
00447         self.assertTrue('libc' in r['Dependencies'])
00448         self.assertTrue('Stacktrace' in r)
00449 
00450         # upload dialog shown
00451         self.assertEqual(self.app.ui_start_upload_progress.call_count, 1)
00452         self.assertEqual(self.app.ui_stop_upload_progress.call_count, 1)
00453 
00454         # URL was opened
00455         self.assertEqual(self.app.open_url.call_count, 1)
00456 
00457     @patch.object(GTKUserInterface, 'open_url')
00458     @patch.object(GTKUserInterface, 'ui_start_upload_progress')
00459     @patch.object(GTKUserInterface, 'ui_stop_upload_progress')
00460     def test_crash_details(self, *args):
00461         '''Crash report with showing details'''
00462 
00463         self.visible_progress = None
00464 
00465         def show_details(*args):
00466             if not self.app.w('show_details').get_visible():
00467                 return True
00468             self.app.w('show_details').clicked()
00469             GLib.timeout_add(200, cont)
00470             return False
00471 
00472         def cont(*args):
00473             # wait until data collection is done and tree filled
00474             if self.app.tree_model.get_iter_first() is None:
00475                 return True
00476 
00477             self.assertTrue(self.app.w('continue_button').get_visible())
00478             self.app.w('continue_button').clicked()
00479             GLib.timeout_add(500, check_progress)
00480             return False
00481 
00482         def check_progress(*args):
00483             self.visible_progress = self.app.w(
00484                 'window_information_collection').get_property('visible')
00485             return False
00486 
00487         GLib.timeout_add(200, show_details)
00488         self.app.run_crash(self.app.report_file)
00489 
00490         # we should have reported one crash
00491         self.assertEqual(self.app.crashdb.latest_id(), 0)
00492         r = self.app.crashdb.download(0)
00493         self.assertEqual(r['ProblemType'], 'Crash')
00494         self.assertEqual(r['ExecutablePath'], '/bin/bash')
00495 
00496         # we already collected details, do not show the progress dialog again
00497         self.assertEqual(self.visible_progress, False)
00498 
00499         # data was collected
00500         self.assertTrue(r['Package'].startswith('bash '))
00501         self.assertTrue('libc' in r['Dependencies'])
00502         self.assertTrue('Stacktrace' in r)
00503 
00504         # upload dialog shown
00505         self.assertEqual(self.app.ui_start_upload_progress.call_count, 1)
00506         self.assertEqual(self.app.ui_stop_upload_progress.call_count, 1)
00507 
00508         # URL was opened
00509         self.assertEqual(self.app.open_url.call_count, 1)
00510 
00511     @patch.object(GTKUserInterface, 'open_url')
00512     def test_crash_noaccept(self, *args):
00513         '''Crash report with non-accepting crash DB'''
00514 
00515         self.visible_progress = None
00516 
00517         def cont(*args):
00518             if not self.app.w('continue_button').get_visible():
00519                 return True
00520             self.app.w('continue_button').clicked()
00521             GLib.timeout_add(500, check_progress)
00522             return False
00523 
00524         def check_progress(*args):
00525             self.visible_progress = self.app.w(
00526                 'window_information_collection').get_property('visible')
00527             return False
00528 
00529         GLib.timeout_add_seconds(1, cont)
00530         self.app.crashdb.options['problem_types'] = ['bug']
00531         self.app.run_crash(self.app.report_file)
00532 
00533         # we should not have reported the crash
00534         self.assertEqual(self.app.crashdb.latest_id(), -1)
00535         self.assertEqual(self.app.open_url.call_count, 0)
00536 
00537         # no progress dialog for non-accepting DB
00538         self.assertEqual(self.visible_progress, False)
00539 
00540         # data was collected for whoopsie
00541         r = self.app.report
00542         self.assertEqual(r['ProblemType'], 'Crash')
00543         self.assertEqual(r['ExecutablePath'], '/bin/bash')
00544         self.assertTrue(r['Package'].startswith('bash '))
00545         self.assertTrue('libc' in r['Dependencies'])
00546         self.assertTrue('Stacktrace' in r)
00547 
00548     @patch.object(GTKUserInterface, 'open_url')
00549     def test_kerneloops_nodetails(self, *args):
00550         '''Kernel oops report without showing details'''
00551 
00552         def cont(*args):
00553             if not self.app.w('continue_button').get_visible():
00554                 return True
00555             self.app.w('continue_button').clicked()
00556             return False
00557 
00558         # remove the crash from setUp() and create a kernel oops
00559         os.remove(self.app.report_file)
00560         kernel_oops = subprocess.Popen([kernel_oops_path], stdin=subprocess.PIPE)
00561         kernel_oops.communicate(b'Plasma conduit phase misalignment')
00562         self.assertEqual(kernel_oops.returncode, 0)
00563 
00564         GLib.timeout_add_seconds(1, cont)
00565         self.app.run_crashes()
00566 
00567         # we should have reported one crash
00568         self.assertEqual(self.app.crashdb.latest_id(), 0)
00569         r = self.app.crashdb.download(0)
00570         self.assertEqual(r['ProblemType'], 'KernelOops')
00571         self.assertEqual(r['OopsText'], 'Plasma conduit phase misalignment')
00572 
00573         # data was collected
00574         self.assertTrue('linux' in r['Package'])
00575         self.assertTrue('Dependencies' in r)
00576         self.assertTrue('Plasma conduit' in r['Title'])
00577 
00578         # URL was opened
00579         self.assertEqual(self.app.open_url.call_count, 1)
00580 
00581     def test_bug_report_installed_package(self):
00582         '''Bug report for installed package'''
00583 
00584         def c(*args):
00585             if not self.app.w('cancel_button').get_visible():
00586                 return True
00587             self.app.w('cancel_button').clicked()
00588             return False
00589 
00590         self.app.report_file = None
00591         self.app.options.package = 'bash'
00592         GLib.timeout_add_seconds(1, c)
00593         self.app.run_report_bug()
00594 
00595         self.assertEqual(self.app.report['ProblemType'], 'Bug')
00596         self.assertEqual(self.app.report['SourcePackage'], 'bash')
00597         self.assertTrue(self.app.report['Package'].startswith('bash '))
00598         self.assertNotEqual(self.app.report['Dependencies'], '')
00599 
00600     def test_bug_report_uninstalled_package(self):
00601         '''Bug report for uninstalled package'''
00602 
00603         def c(*args):
00604             if not self.app.w('cancel_button').get_visible():
00605                 return True
00606             self.app.w('cancel_button').clicked()
00607             return False
00608 
00609         pkg = apport.packaging.get_uninstalled_package()
00610         self.app.report_file = None
00611         self.app.options.package = pkg
00612         GLib.timeout_add_seconds(1, c)
00613         self.app.run_report_bug()
00614 
00615         self.assertEqual(self.app.report['ProblemType'], 'Bug')
00616         self.assertEqual(self.app.report['SourcePackage'],
00617                          apport.packaging.get_source(pkg))
00618         self.assertEqual(self.app.report['Package'], '%s (not installed)' % pkg)
00619 
00620     @patch.object(GTKUserInterface, 'open_url')
00621     def test_update_report(self, *args):
00622         '''Updating an existing report'''
00623 
00624         self.app.report_file = None
00625 
00626         def cont(*args):
00627             if self.app.tree_model.get_iter_first() is None:
00628                 return True
00629             self.app.w('continue_button').clicked()
00630             return False
00631 
00632         # upload empty report
00633         id = self.app.crashdb.upload({})
00634         self.assertEqual(id, 0)
00635         self.app.options.update_report = 0
00636         self.app.options.package = 'bash'
00637 
00638         GLib.timeout_add(200, cont)
00639         self.app.run_update_report()
00640 
00641         # no new bug reported
00642         self.assertEqual(self.app.crashdb.latest_id(), 0)
00643 
00644         # bug was updated
00645         r = self.app.crashdb.download(0)
00646         self.assertTrue(r['Package'].startswith('bash '))
00647         self.assertTrue('libc' in r['Dependencies'])
00648         self.assertTrue('DistroRelease' in r)
00649 
00650         # No URL in this mode
00651         self.assertEqual(self.app.open_url.call_count, 0)
00652 
00653     @patch.object(GTKUserInterface, 'open_url')
00654     def test_update_report_different_binary_source(self, *args):
00655         '''Updating an existing report on a source package which does not have a binary of the same name'''
00656 
00657         self.app.report_file = None
00658 
00659         def cont(*args):
00660             if self.app.tree_model.get_iter_first() is None:
00661                 return True
00662             self.app.w('continue_button').clicked()
00663             return False
00664 
00665         kernel_pkg = apport.packaging.get_kernel_package()
00666         kernel_src = apport.packaging.get_source(kernel_pkg)
00667         self.assertNotEqual(kernel_pkg, kernel_src,
00668                             'this test assumes that the kernel binary package != kernel source package')
00669         self.assertNotEqual(apport.packaging.get_version(kernel_pkg), '',
00670                             'this test assumes that the kernel binary package %s is installed' % kernel_pkg)
00671         # this test assumes that the kernel source package name is not an
00672         # installed binary package
00673         self.assertRaises(ValueError, apport.packaging.get_version, kernel_src)
00674 
00675         # create source package hook, as otherwise there is nothing to collect
00676         with open(os.path.join(self.hook_dir, 'source_%s.py' % kernel_src), 'w') as f:
00677             f.write('def add_info(r, ui):\n r["MachineType"]="Laptop"\n')
00678 
00679         # upload empty report
00680         id = self.app.crashdb.upload({})
00681         self.assertEqual(id, 0)
00682 
00683         # run in update mode for that bug
00684         self.app.options.update_report = 0
00685         self.app.options.package = kernel_src
00686 
00687         GLib.timeout_add(200, cont)
00688         self.app.run_update_report()
00689 
00690         # no new bug reported
00691         self.assertEqual(self.app.crashdb.latest_id(), 0)
00692 
00693         # bug was updated
00694         r = self.app.crashdb.download(0)
00695         self.assertTrue('ProcEnviron' in r)
00696         self.assertTrue('DistroRelease' in r)
00697         self.assertTrue('Uname' in r)
00698         self.assertEqual(r['MachineType'], 'Laptop')
00699 
00700         # No URL in this mode
00701         self.assertEqual(self.app.open_url.call_count, 0)
00702 
00703     @patch.object(GTKUserInterface, 'get_desktop_entry')
00704     def test_missing_icon(self, *args):
00705         # LP: 937354
00706         self.app.report['ProblemType'] = 'Crash'
00707         self.app.report['Package'] = 'apport 1.2.3~0ubuntu1'
00708         self.app.get_desktop_entry.return_value = {'name': 'apport', 'icon': 'nonexistent'}
00709         GLib.idle_add(Gtk.main_quit)
00710         self.app.ui_present_report_details(True)
00711 
00712     def test_resizing(self):
00713         '''Problem report window resizability and sizing.'''
00714 
00715         def show_details(data):
00716             if not self.app.w('show_details').get_visible():
00717                 return True
00718 
00719             data['orig_size'] = self.app.w('dialog_crash_new').get_size()
00720             data['orig_resizable'] = self.app.w('dialog_crash_new').get_resizable()
00721             self.app.w('show_details').clicked()
00722             GLib.timeout_add(200, hide_details, data)
00723             return False
00724 
00725         def hide_details(data):
00726             # wait until data collection is done and tree filled
00727             if self.app.tree_model.get_iter_first() is None:
00728                 return True
00729 
00730             data['detail_size'] = self.app.w('dialog_crash_new').get_size()
00731             data['detail_resizable'] = self.app.w('dialog_crash_new').get_resizable()
00732             self.app.w('show_details').clicked()
00733             GLib.timeout_add(200, details_hidden, data)
00734             return False
00735 
00736         def details_hidden(data):
00737             # wait until data collection is done and tree filled
00738             if self.app.w('details_scrolledwindow').get_visible():
00739                 return True
00740 
00741             data['hidden_size'] = self.app.w('dialog_crash_new').get_size()
00742             data['hidden_resizable'] = self.app.w('dialog_crash_new').get_resizable()
00743             Gtk.main_quit()
00744 
00745         data = {}
00746         GLib.timeout_add(200, show_details, data)
00747         self.app.run_crash(self.app.report_file)
00748 
00749         # when showing details, dialog should get considerably bigger
00750         self.assertGreater(data['detail_size'][1], data['orig_size'][1] + 100)
00751 
00752         # when hiding details, original size should be restored
00753         self.assertEqual(data['orig_size'], data['hidden_size'])
00754 
00755         # should only be resizable in details mode
00756         self.assertFalse(data['orig_resizable'])
00757         self.assertTrue(data['detail_resizable'])
00758         self.assertFalse(data['hidden_resizable'])
00759 
00760     def test_dialog_nonascii(self):
00761         '''Non-ASCII title/text in dialogs'''
00762 
00763         def close(response):
00764             if not self.app.md:
00765                 return True
00766             self.app.md.response(response)
00767             return False
00768 
00769         # unicode arguments
00770         GLib.timeout_add(200, close, 0)
00771         self.app.ui_info_message(b'title \xe2\x99\xaa'.decode('UTF-8'), b'text \xe2\x99\xaa'.decode('UTF-8'))
00772         # byte array arguments (in Python 2)
00773         if sys.version < '3':
00774             GLib.timeout_add(200, close, 0)
00775             self.app.ui_info_message(b'title \xe2\x99\xaa', b'text \xe2\x99\xaa')
00776 
00777         # with URLs
00778         GLib.timeout_add(200, close, 0)
00779         self.app.ui_info_message('title', b'http://example.com \xe2\x99\xaa'.decode('UTF-8'))
00780         if sys.version < '3':
00781             GLib.timeout_add(200, close, 0)
00782             self.app.ui_info_message('title', b'http://example.com \xe2\x99\xaa')
00783 
00784     def test_immediate_close(self):
00785         '''Close details window immediately'''
00786 
00787         # this reproduces https://launchpad.net/bugs/938090
00788         self.app.w('dialog_crash_new').destroy()
00789         GLib.idle_add(Gtk.main_quit)
00790         self.app.run_crash(self.app.report_file)
00791 
00792     @patch.object(GTKUserInterface, 'ui_start_upload_progress')
00793     def test_close_during_collect(self, *args):
00794         '''Close details window during information collection'''
00795 
00796         def show_details(*args):
00797             if not self.app.w('show_details').get_visible():
00798                 return True
00799             self.app.w('show_details').clicked()
00800             GLib.timeout_add(200, close)
00801             return False
00802 
00803         def close(*args):
00804             self.app.w('dialog_crash_new').destroy()
00805             return False
00806 
00807         GLib.timeout_add(200, show_details)
00808         self.app.run_crash(self.app.report_file)
00809 
00810         self.assertEqual(self.app.ui_start_upload_progress.call_count, 0)
00811 
00812 unittest.main()