Back to index

apport  2.4
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_recoverable_crash_layout(self):
00407         '''
00408         +-----------------------------------------------------------------+
00409         | [ logo ] The application Foo has experienced an internal error. |
00410         |          Developer-specified error text.                        |
00411         |                                                                 |
00412         |            [x] Send an error report to help fix this problem.   |
00413         |                                                                 |
00414         | [ Show Details ]                                   [ Continue ] |
00415         +-----------------------------------------------------------------+
00416         '''
00417         self.app.report['ProblemType'] = 'RecoverableProblem'
00418         self.app.report['Package'] = 'apport 1.2.3~0ubuntu1'
00419         self.app.report['DialogBody'] = 'Some developer-specified error text.'
00420         with tempfile.NamedTemporaryFile() as fp:
00421             fp.write(b'''[Desktop Entry]
00422 Version=1.0
00423 Name=Apport
00424 Type=Application''')
00425             fp.flush()
00426             self.app.report['DesktopFile'] = fp.name
00427             GLib.idle_add(Gtk.main_quit)
00428             self.app.ui_present_report_details(True)
00429         self.assertEqual(self.app.w('dialog_crash_new').get_title(),
00430                          self.distro)
00431         msg = 'The application Apport has experienced an internal error.'
00432         self.assertEqual(self.app.w('title_label').get_text(), msg)
00433         msg = 'Some developer-specified error text.'
00434         self.assertEqual(self.app.w('subtitle_label').get_text(), msg)
00435         self.assertTrue(self.app.w('subtitle_label').get_property('visible'))
00436         send_error_report = self.app.w('send_error_report')
00437         self.assertTrue(send_error_report.get_property('visible'))
00438         self.assertTrue(send_error_report.get_active())
00439         self.assertTrue(self.app.w('show_details').get_property('visible'))
00440         self.assertTrue(self.app.w('continue_button').get_property('visible'))
00441         self.assertEqual(self.app.w('continue_button').get_label(),
00442                          _('Continue'))
00443         self.assertFalse(self.app.w('closed_button').get_property('visible'))
00444 
00445     def test_administrator_disabled_reporting(self):
00446         GLib.idle_add(Gtk.main_quit)
00447         self.app.ui_present_report_details(False)
00448         send_error_report = self.app.w('send_error_report')
00449         self.assertFalse(send_error_report.get_property('visible'))
00450         self.assertFalse(send_error_report.get_active())
00451 
00452     @patch.object(GTKUserInterface, 'open_url')
00453     @patch.object(GTKUserInterface, 'ui_start_upload_progress')
00454     @patch.object(GTKUserInterface, 'ui_stop_upload_progress')
00455     def test_crash_nodetails(self, *args):
00456         '''Crash report without showing details'''
00457 
00458         self.visible_progress = None
00459 
00460         def cont(*args):
00461             if not self.app.w('continue_button').get_visible():
00462                 return True
00463             self.app.w('continue_button').clicked()
00464             GLib.timeout_add(500, check_progress)
00465             return False
00466 
00467         def check_progress(*args):
00468             self.visible_progress = self.app.w(
00469                 'window_information_collection').get_property('visible')
00470             return False
00471 
00472         GLib.timeout_add_seconds(1, cont)
00473         self.app.run_crash(self.app.report_file)
00474 
00475         # we should have reported one crash
00476         self.assertEqual(self.app.crashdb.latest_id(), 0)
00477         r = self.app.crashdb.download(0)
00478         self.assertEqual(r['ProblemType'], 'Crash')
00479         self.assertEqual(r['ExecutablePath'], '/bin/bash')
00480 
00481         # should show a progress bar for info collection
00482         self.assertEqual(self.visible_progress, True)
00483 
00484         # data was collected
00485         self.assertTrue(r['Package'].startswith('bash '))
00486         self.assertTrue('libc' in r['Dependencies'])
00487         self.assertTrue('Stacktrace' in r)
00488 
00489         # upload dialog shown
00490         self.assertEqual(self.app.ui_start_upload_progress.call_count, 1)
00491         self.assertEqual(self.app.ui_stop_upload_progress.call_count, 1)
00492 
00493         # URL was opened
00494         self.assertEqual(self.app.open_url.call_count, 1)
00495 
00496     @patch.object(GTKUserInterface, 'open_url')
00497     @patch.object(GTKUserInterface, 'ui_start_upload_progress')
00498     @patch.object(GTKUserInterface, 'ui_stop_upload_progress')
00499     def test_crash_details(self, *args):
00500         '''Crash report with showing details'''
00501 
00502         self.visible_progress = None
00503 
00504         def show_details(*args):
00505             if not self.app.w('show_details').get_visible():
00506                 return True
00507             self.app.w('show_details').clicked()
00508             GLib.timeout_add(200, cont)
00509             return False
00510 
00511         def cont(*args):
00512             # wait until data collection is done and tree filled
00513             if self.app.tree_model.get_iter_first() is None:
00514                 return True
00515 
00516             self.assertTrue(self.app.w('continue_button').get_visible())
00517             self.app.w('continue_button').clicked()
00518             GLib.timeout_add(500, check_progress)
00519             return False
00520 
00521         def check_progress(*args):
00522             self.visible_progress = self.app.w(
00523                 'window_information_collection').get_property('visible')
00524             return False
00525 
00526         GLib.timeout_add(200, show_details)
00527         self.app.run_crash(self.app.report_file)
00528 
00529         # we should have reported one crash
00530         self.assertEqual(self.app.crashdb.latest_id(), 0)
00531         r = self.app.crashdb.download(0)
00532         self.assertEqual(r['ProblemType'], 'Crash')
00533         self.assertEqual(r['ExecutablePath'], '/bin/bash')
00534 
00535         # we already collected details, do not show the progress dialog again
00536         self.assertEqual(self.visible_progress, False)
00537 
00538         # data was collected
00539         self.assertTrue(r['Package'].startswith('bash '))
00540         self.assertTrue('libc' in r['Dependencies'])
00541         self.assertTrue('Stacktrace' in r)
00542 
00543         # upload dialog shown
00544         self.assertEqual(self.app.ui_start_upload_progress.call_count, 1)
00545         self.assertEqual(self.app.ui_stop_upload_progress.call_count, 1)
00546 
00547         # URL was opened
00548         self.assertEqual(self.app.open_url.call_count, 1)
00549 
00550     @patch.object(GTKUserInterface, 'open_url')
00551     def test_crash_noaccept(self, *args):
00552         '''Crash report with non-accepting crash DB'''
00553 
00554         self.visible_progress = None
00555 
00556         def cont(*args):
00557             if not self.app.w('continue_button').get_visible():
00558                 return True
00559             self.app.w('continue_button').clicked()
00560             GLib.timeout_add(500, check_progress)
00561             return False
00562 
00563         def check_progress(*args):
00564             self.visible_progress = self.app.w(
00565                 'window_information_collection').get_property('visible')
00566             return False
00567 
00568         GLib.timeout_add_seconds(1, cont)
00569         self.app.crashdb.options['problem_types'] = ['bug']
00570         self.app.run_crash(self.app.report_file)
00571 
00572         # we should not have reported the crash
00573         self.assertEqual(self.app.crashdb.latest_id(), -1)
00574         self.assertEqual(self.app.open_url.call_count, 0)
00575 
00576         # no progress dialog for non-accepting DB
00577         self.assertEqual(self.visible_progress, False)
00578 
00579         # data was collected for whoopsie
00580         r = self.app.report
00581         self.assertEqual(r['ProblemType'], 'Crash')
00582         self.assertEqual(r['ExecutablePath'], '/bin/bash')
00583         self.assertTrue(r['Package'].startswith('bash '))
00584         self.assertTrue('libc' in r['Dependencies'])
00585         self.assertTrue('Stacktrace' in r)
00586 
00587     @patch.object(GTKUserInterface, 'open_url')
00588     def test_kerneloops_nodetails(self, *args):
00589         '''Kernel oops report without showing details'''
00590 
00591         def cont(*args):
00592             if not self.app.w('continue_button').get_visible():
00593                 return True
00594             self.app.w('continue_button').clicked()
00595             return False
00596 
00597         # remove the crash from setUp() and create a kernel oops
00598         os.remove(self.app.report_file)
00599         kernel_oops = subprocess.Popen([kernel_oops_path], stdin=subprocess.PIPE)
00600         kernel_oops.communicate(b'Plasma conduit phase misalignment')
00601         self.assertEqual(kernel_oops.returncode, 0)
00602 
00603         GLib.timeout_add_seconds(1, cont)
00604         self.app.run_crashes()
00605 
00606         # we should have reported one crash
00607         self.assertEqual(self.app.crashdb.latest_id(), 0)
00608         r = self.app.crashdb.download(0)
00609         self.assertEqual(r['ProblemType'], 'KernelOops')
00610         self.assertEqual(r['OopsText'], 'Plasma conduit phase misalignment')
00611 
00612         # data was collected
00613         self.assertTrue('linux' in r['Package'])
00614         self.assertTrue('Dependencies' in r)
00615         self.assertTrue('Plasma conduit' in r['Title'])
00616 
00617         # URL was opened
00618         self.assertEqual(self.app.open_url.call_count, 1)
00619 
00620     def test_bug_report_installed_package(self):
00621         '''Bug report for installed package'''
00622 
00623         def c(*args):
00624             if not self.app.w('cancel_button').get_visible():
00625                 return True
00626             self.app.w('cancel_button').clicked()
00627             return False
00628 
00629         self.app.report_file = None
00630         self.app.options.package = 'bash'
00631         GLib.timeout_add_seconds(1, c)
00632         self.app.run_report_bug()
00633 
00634         self.assertEqual(self.app.report['ProblemType'], 'Bug')
00635         self.assertEqual(self.app.report['SourcePackage'], 'bash')
00636         self.assertTrue(self.app.report['Package'].startswith('bash '))
00637         self.assertNotEqual(self.app.report['Dependencies'], '')
00638 
00639     def test_bug_report_uninstalled_package(self):
00640         '''Bug report for uninstalled package'''
00641 
00642         def c(*args):
00643             if not self.app.w('cancel_button').get_visible():
00644                 return True
00645             self.app.w('cancel_button').clicked()
00646             return False
00647 
00648         pkg = apport.packaging.get_uninstalled_package()
00649         self.app.report_file = None
00650         self.app.options.package = pkg
00651         GLib.timeout_add_seconds(1, c)
00652         self.app.run_report_bug()
00653 
00654         self.assertEqual(self.app.report['ProblemType'], 'Bug')
00655         self.assertEqual(self.app.report['SourcePackage'],
00656                          apport.packaging.get_source(pkg))
00657         self.assertEqual(self.app.report['Package'], '%s (not installed)' % pkg)
00658 
00659     @patch.object(GTKUserInterface, 'open_url')
00660     def test_update_report(self, *args):
00661         '''Updating an existing report'''
00662 
00663         self.app.report_file = None
00664 
00665         def cont(*args):
00666             if self.app.tree_model.get_iter_first() is None:
00667                 return True
00668             self.app.w('continue_button').clicked()
00669             return False
00670 
00671         # upload empty report
00672         id = self.app.crashdb.upload({})
00673         self.assertEqual(id, 0)
00674         self.app.options.update_report = 0
00675         self.app.options.package = 'bash'
00676 
00677         GLib.timeout_add(200, cont)
00678         self.app.run_update_report()
00679 
00680         # no new bug reported
00681         self.assertEqual(self.app.crashdb.latest_id(), 0)
00682 
00683         # bug was updated
00684         r = self.app.crashdb.download(0)
00685         self.assertTrue(r['Package'].startswith('bash '))
00686         self.assertTrue('libc' in r['Dependencies'])
00687         self.assertTrue('DistroRelease' in r)
00688 
00689         # No URL in this mode
00690         self.assertEqual(self.app.open_url.call_count, 0)
00691 
00692     @patch.object(GTKUserInterface, 'open_url')
00693     def test_update_report_different_binary_source(self, *args):
00694         '''Updating an existing report on a source package which does not have a binary of the same name'''
00695 
00696         self.app.report_file = None
00697 
00698         def cont(*args):
00699             if self.app.tree_model.get_iter_first() is None:
00700                 return True
00701             self.app.w('continue_button').clicked()
00702             return False
00703 
00704         kernel_pkg = apport.packaging.get_kernel_package()
00705         kernel_src = apport.packaging.get_source(kernel_pkg)
00706         self.assertNotEqual(kernel_pkg, kernel_src,
00707                             'this test assumes that the kernel binary package != kernel source package')
00708         self.assertNotEqual(apport.packaging.get_version(kernel_pkg), '',
00709                             'this test assumes that the kernel binary package %s is installed' % kernel_pkg)
00710         # this test assumes that the kernel source package name is not an
00711         # installed binary package
00712         self.assertRaises(ValueError, apport.packaging.get_version, kernel_src)
00713 
00714         # create source package hook, as otherwise there is nothing to collect
00715         with open(os.path.join(self.hook_dir, 'source_%s.py' % kernel_src), 'w') as f:
00716             f.write('def add_info(r, ui):\n r["MachineType"]="Laptop"\n')
00717 
00718         # upload empty report
00719         id = self.app.crashdb.upload({})
00720         self.assertEqual(id, 0)
00721 
00722         # run in update mode for that bug
00723         self.app.options.update_report = 0
00724         self.app.options.package = kernel_src
00725 
00726         GLib.timeout_add(200, cont)
00727         self.app.run_update_report()
00728 
00729         # no new bug reported
00730         self.assertEqual(self.app.crashdb.latest_id(), 0)
00731 
00732         # bug was updated
00733         r = self.app.crashdb.download(0)
00734         self.assertTrue('ProcEnviron' in r)
00735         self.assertTrue('DistroRelease' in r)
00736         self.assertTrue('Uname' in r)
00737         self.assertEqual(r['MachineType'], 'Laptop')
00738 
00739         # No URL in this mode
00740         self.assertEqual(self.app.open_url.call_count, 0)
00741 
00742     @patch.object(GTKUserInterface, 'get_desktop_entry')
00743     def test_missing_icon(self, *args):
00744         # LP: 937354
00745         self.app.report['ProblemType'] = 'Crash'
00746         self.app.report['Package'] = 'apport 1.2.3~0ubuntu1'
00747         self.app.get_desktop_entry.return_value = {'name': 'apport', 'icon': 'nonexistent'}
00748         GLib.idle_add(Gtk.main_quit)
00749         self.app.ui_present_report_details(True)
00750 
00751     def test_resizing(self):
00752         '''Problem report window resizability and sizing.'''
00753 
00754         def show_details(data):
00755             if not self.app.w('show_details').get_visible():
00756                 return True
00757 
00758             data['orig_size'] = self.app.w('dialog_crash_new').get_size()
00759             data['orig_resizable'] = self.app.w('dialog_crash_new').get_resizable()
00760             self.app.w('show_details').clicked()
00761             GLib.timeout_add(200, hide_details, data)
00762             return False
00763 
00764         def hide_details(data):
00765             # wait until data collection is done and tree filled
00766             if self.app.tree_model.get_iter_first() is None:
00767                 return True
00768 
00769             data['detail_size'] = self.app.w('dialog_crash_new').get_size()
00770             data['detail_resizable'] = self.app.w('dialog_crash_new').get_resizable()
00771             self.app.w('show_details').clicked()
00772             GLib.timeout_add(200, details_hidden, data)
00773             return False
00774 
00775         def details_hidden(data):
00776             # wait until data collection is done and tree filled
00777             if self.app.w('details_scrolledwindow').get_visible():
00778                 return True
00779 
00780             data['hidden_size'] = self.app.w('dialog_crash_new').get_size()
00781             data['hidden_resizable'] = self.app.w('dialog_crash_new').get_resizable()
00782             Gtk.main_quit()
00783 
00784         data = {}
00785         GLib.timeout_add(200, show_details, data)
00786         self.app.run_crash(self.app.report_file)
00787 
00788         # when showing details, dialog should get considerably bigger
00789         self.assertGreater(data['detail_size'][1], data['orig_size'][1] + 100)
00790 
00791         # when hiding details, original size should be restored
00792         self.assertEqual(data['orig_size'], data['hidden_size'])
00793 
00794         # should only be resizable in details mode
00795         self.assertFalse(data['orig_resizable'])
00796         self.assertTrue(data['detail_resizable'])
00797         self.assertFalse(data['hidden_resizable'])
00798 
00799     def test_dialog_nonascii(self):
00800         '''Non-ASCII title/text in dialogs'''
00801 
00802         def close(response):
00803             if not self.app.md:
00804                 return True
00805             self.app.md.response(response)
00806             return False
00807 
00808         # unicode arguments
00809         GLib.timeout_add(200, close, 0)
00810         self.app.ui_info_message(b'title \xe2\x99\xaa'.decode('UTF-8'), b'text \xe2\x99\xaa'.decode('UTF-8'))
00811         # byte array arguments (in Python 2)
00812         if sys.version < '3':
00813             GLib.timeout_add(200, close, 0)
00814             self.app.ui_info_message(b'title \xe2\x99\xaa', b'text \xe2\x99\xaa')
00815 
00816         # with URLs
00817         GLib.timeout_add(200, close, 0)
00818         self.app.ui_info_message('title', b'http://example.com \xe2\x99\xaa'.decode('UTF-8'))
00819         if sys.version < '3':
00820             GLib.timeout_add(200, close, 0)
00821             self.app.ui_info_message('title', b'http://example.com \xe2\x99\xaa')
00822 
00823     def test_immediate_close(self):
00824         '''Close details window immediately'''
00825 
00826         # this reproduces https://launchpad.net/bugs/938090
00827         self.app.w('dialog_crash_new').destroy()
00828         GLib.idle_add(Gtk.main_quit)
00829         self.app.run_crash(self.app.report_file)
00830 
00831     @patch.object(GTKUserInterface, 'ui_start_upload_progress')
00832     def test_close_during_collect(self, *args):
00833         '''Close details window during information collection'''
00834 
00835         def show_details(*args):
00836             if not self.app.w('show_details').get_visible():
00837                 return True
00838             self.app.w('show_details').clicked()
00839             GLib.timeout_add(200, close)
00840             return False
00841 
00842         def close(*args):
00843             self.app.w('dialog_crash_new').destroy()
00844             return False
00845 
00846         GLib.timeout_add(200, show_details)
00847         self.app.run_crash(self.app.report_file)
00848 
00849         self.assertEqual(self.app.ui_start_upload_progress.call_count, 0)
00850 
00851 unittest.main()