~brian-murray/ubuntu/natty/apport/rr-if-idontknow

« back to all changes in this revision

Viewing changes to apport/ui.py

  • Committer: Martin Pitt
  • Date: 2011-02-04 14:41:46 UTC
  • mfrom: (1369.34.52 trunk)
  • Revision ID: martin.pitt@canonical.com-20110204144146-o0fq0kh4shzsutr3
new upstream release 1.17.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
13
13
# option) any later version.  See http://www.gnu.org/copyleft/gpl.html for
14
14
# the full text of the license.
15
15
 
16
 
__version__ = '1.17.1'
 
16
__version__ = '1.17.2'
17
17
 
18
18
import glob, sys, os.path, optparse, time, traceback, locale, gettext, re
19
19
import pwd, errno, urllib, zlib
54
54
            execfile(symptom_script, symb)
55
55
            package = symb['run'](report, ui)
56
56
            if not package:
57
 
                print >> sys.stderr, 'symptom script %s did not determine the affected package' % symptom_script
 
57
                apport.error('symptom script %s did not determine the affected package', symptom_script)
58
58
                return
59
59
            report['Symptom'] = os.path.splitext(os.path.basename(symptom_script))[0]
60
60
        except StopIteration:
61
61
            sys.exit(0)
62
62
        except:
63
 
            print >> sys.stderr, 'symptom script %s crashed:' % symptom_script
 
63
            apport.error('symptom script %s crashed:', symptom_script)
64
64
            traceback.print_exc()
65
65
            sys.exit(0)
66
66
 
68
68
        if report.has_key('ExecutablePath'):
69
69
            package = apport.fileutils.find_file_package(report['ExecutablePath'])
70
70
        else:
71
 
            raise KeyError, 'called without a package, and report does not have ExecutablePath'
 
71
            raise KeyError('called without a package, and report does not have ExecutablePath')
72
72
    try:
73
73
        report.add_package_info(package)
74
74
    except ValueError:
76
76
        # package
77
77
        if not ignore_uninstalled:
78
78
            raise
79
 
    except SystemError, e:
 
79
    except SystemError as e:
80
80
        report['UnreportableReason'] = excstr(e)
81
81
        return
82
82
 
125
125
        report.write(f, only_new=True)
126
126
        f.close()
127
127
        apport.fileutils.mark_report_seen(reportfile)
128
 
        os.chmod (reportfile, 0600)
 
128
        os.chmod (reportfile, 0o600)
129
129
 
130
130
class UserInterface:
131
131
    '''Apport user interface API.
145
145
 
146
146
        try:
147
147
            self.crashdb = get_crashdb(None)
148
 
        except ImportError, e:
 
148
        except ImportError as e:
149
149
            # this can happen while upgrading python packages
150
 
            print >> sys.stderr, 'Could not import module, is a package upgrade in progress? Error:', e
151
 
            sys.exit(1)
 
150
            apport.fatal('Could not import module, is a package upgrade in progress? Error: %s', str(e))
152
151
        except KeyError:
153
 
            print >> sys.stderr, '/etc/apport/crashdb.conf is damaged: No default database'
154
 
            sys.exit(1)
 
152
            apport.fatal('/etc/apport/crashdb.conf is damaged: No default database')
155
153
 
156
154
        gettext.textdomain(self.gettext_domain)
157
155
        self.parse_argv()
262
260
            try:
263
261
                if 'Dependencies' not in self.report:
264
262
                    self.collect_info()
265
 
            except (IOError, zlib.error), e:
 
263
            except (IOError, zlib.error) as e:
266
264
                # can happen with broken core dumps
267
265
                self.report = None
268
266
                self.ui_error_message(_('Invalid problem report'),
299
297
                assert response == 'full'
300
298
 
301
299
            self.file_report()
302
 
        except IOError, e:
 
300
        except IOError as e:
303
301
            # fail gracefully if file is not readable for us
304
302
            if e.errno in (errno.EPERM, errno.EACCES):
305
303
                self.ui_error_message(_('Invalid problem report'),
312
310
            else:
313
311
                self.ui_error_message(_('Invalid problem report'), e.strerror)
314
312
                sys.exit(1)
315
 
        except OSError, e:
 
313
        except OSError as e:
316
314
            # fail gracefully on ENOMEM
317
315
            if e.errno == errno.ENOMEM:
318
 
                print >> sys.stderr, 'Out of memory, aborting'
319
 
                sys.exit(1)
 
316
                apport.fatal('Out of memory, aborting')
320
317
            else:
321
318
                raise
322
319
 
349
346
                self.ui_error_message(_('Invalid PID'),
350
347
                        _('The specified process ID does not belong to a program.'))
351
348
                return False
352
 
            except OSError, e:
 
349
            except OSError as e:
353
350
                # silently ignore nonexisting PIDs; the user must not close the
354
351
                # application prematurely
355
352
                if e.errno == errno.ENOENT:
373
370
 
374
371
        try:
375
372
            self.collect_info(symptom_script)
376
 
        except ValueError, e:
 
373
        except ValueError as e:
377
374
            if str(e) == 'package does not exist':
378
375
                if not self.cur_package:
379
376
                    self.ui_error_message(_('Invalid problem report'), 
392
389
                self.report['UnreportableReason'])
393
390
            return
394
391
 
 
392
        self.add_extra_tags()
 
393
 
395
394
        if self.handle_duplicate():
396
395
            return True
397
396
 
406
405
                f = open(os.path.expanduser(self.options.save), 'w')
407
406
                self.report.write(f)
408
407
                f.close()
409
 
            except (IOError, OSError), e:
 
408
            except (IOError, OSError) as e:
410
409
                self.ui_error_message(_('Cannot create report'), excstr(e))
411
410
        else:
412
411
            # show what's being sent
450
449
 
451
450
        info_collected = False
452
451
        for p in pkgs:
453
 
            #print 'Collecting apport information for source package %s...' % p
 
452
            #print('Collecting apport information for source package %s...' % p)
454
453
            self.cur_package = p
455
454
            self.report['SourcePackage'] = p
456
455
            self.report['Package'] = p # no way to find this out
461
460
                apport.packaging.get_version(p)
462
461
            except ValueError:
463
462
                if not os.path.exists(os.path.join(apport.report._hook_dir, 'source_%s.py' % p)):
464
 
                    print 'Package %s not installed and no hook available, ignoring' % p
 
463
                    print('Package %s not installed and no hook available, ignoring' % p)
465
464
                    continue
466
465
            self.collect_info(ignore_uninstalled=True)
467
466
            info_collected = True
473
472
 
474
473
        self.report.add_user_info()
475
474
        self.report.add_proc_environ()
 
475
        self.add_extra_tags()
476
476
 
477
477
        # delete the uninteresting keys
478
478
        del self.report['ProblemType']
511
511
            try:
512
512
                execfile(script, symb)
513
513
            except:
514
 
                print >> sys.stderr, 'symptom script %s is invalid' % script
 
514
                apport.error('symptom script %s is invalid', script)
515
515
                traceback.print_exc()
516
516
                continue
517
517
            symptom_names.append(os.path.splitext(os.path.basename(script))[0])
564
564
        elif self.options.update_report:
565
565
            return self.run_update_report()
566
566
        elif self.options.version:
567
 
            print __version__
 
567
            print(__version__)
568
568
            return True
569
569
        elif self.options.crash_file:
570
570
            try:
571
571
                self.run_crash(self.options.crash_file, False)
572
 
            except OSError, e:
 
572
            except OSError as e:
573
573
                self.ui_error_message(_('Invalid problem report'), excstr(e))
574
574
            return True
575
575
        else:
586
586
        '''
587
587
        optparser = optparse.OptionParser(_('%prog <report number>'))
588
588
        optparser.add_option('-p', '--package',
589
 
            help=_('Specify package name.)'),
590
 
            dest='package', default=None)
 
589
            help=_('Specify package name.)'))
 
590
        optparser.add_option('--tag', action='append', default=[],
 
591
            help=_('Add an extra tag to the report. Can be specified multiple times.'))
591
592
        (self.options, self.args) = optparser.parse_args()
592
593
 
593
594
        if len(self.args) != 1 or not self.args[0].isdigit():
618
619
                return
619
620
 
620
621
        optparser = optparse.OptionParser(_('%prog [options] [symptom|pid|package|program path|.apport/.crash file]'))
621
 
        optparser.add_option('-f', '--file-bug',
622
 
            help=_('Start in bug filing mode. Requires --package and an optional --pid, or just a --pid. If neither is given, display a list of known symptoms. (Implied if a single argument is given.)'),
623
 
            action='store_true', dest='filebug', default=False)
624
 
        optparser.add_option('-u', '--update-bug', type='int',
625
 
            help=_('Start in bug updating mode. Can take an optional --package.'),
626
 
            dest='update_report')
 
622
        optparser.add_option('-f', '--file-bug', action='store_true',
 
623
            dest='filebug', default=False,
 
624
            help=_('Start in bug filing mode. Requires --package and an optional --pid, or just a --pid. If neither is given, display a list of known symptoms. (Implied if a single argument is given.)'))
 
625
        optparser.add_option('-u', '--update-bug', type='int', dest='update_report',
 
626
            help=_('Start in bug updating mode. Can take an optional --package.'))
627
627
        optparser.add_option('-s', '--symptom', metavar='SYMPTOM',
628
 
            help=_('File a bug report about a symptom. (Implied if symptom name is given as only argument.)'), 
629
 
            dest='symptom')
 
628
            help=_('File a bug report about a symptom. (Implied if symptom name is given as only argument.)'))
630
629
        optparser.add_option('-p', '--package',
631
 
            help=_('Specify package name in --file-bug mode. This is optional if a --pid is specified. (Implied if package name is given as only argument.)'),
632
 
            action='store', type='string', dest='package', default=None)
633
 
        optparser.add_option('-P', '--pid',
634
 
            help=_('Specify a running program in --file-bug mode. If this is specified, the bug report will contain more information. (Implied if pid is given as only argument.)'),
635
 
            action='store', type='int', dest='pid', default=None)
636
 
        optparser.add_option('-c', '--crash-file',
637
 
            help=_('Report the crash from given .apport or .crash file instead of the pending ones in %s. (Implied if file is given as only argument.)') % apport.fileutils.report_dir,
638
 
            action='store', type='string', dest='crash_file', default=None, metavar='PATH')
639
 
        optparser.add_option('--save',
640
 
            help=_('In --file-bug mode, save the collected information into a file instead of reporting it. This file can then be reported with --crash-file later on.'),
641
 
            type='string', dest='save', default=None, metavar='PATH')
642
 
        optparser.add_option('-v', '--version',
643
 
            help=_('Print the Apport version number.'),
644
 
            action='store_true', dest='version', default=None)
 
630
            help=_('Specify package name in --file-bug mode. This is optional if a --pid is specified. (Implied if package name is given as only argument.)'))
 
631
        optparser.add_option('-P', '--pid', type='int',
 
632
            help=_('Specify a running program in --file-bug mode. If this is specified, the bug report will contain more information.  (Implied if pid is given as only argument.)'))
 
633
        optparser.add_option('-c', '--crash-file', metavar='PATH',
 
634
            help=_('Report the crash from given .apport or .crash file instead of the pending ones in %s. (Implied if file is given as only argument.)') % apport.fileutils.report_dir)
 
635
        optparser.add_option('--save', metavar='PATH',
 
636
            help=_('In bug filing mode, save the collected information into a file instead of reporting it. This file can then be reported later on from a different machine.'))
 
637
        optparser.add_option('--tag', action='append', default=[],
 
638
            help=_('Add an extra tag to the report. Can be specified multiple times.'))
 
639
        optparser.add_option('-v', '--version', action='store_true',
 
640
            help=_('Print the Apport version number.'))
 
641
 
 
642
        if len(sys.argv) > 0 and cmd.endswith('-bug'):
 
643
            for o in ('-f', '-u', '-s', '-p', '-P', '-c'):
 
644
                optparser.get_option(o).help = optparse.SUPPRESS_HELP
645
645
 
646
646
        (self.options, self.args) = optparser.parse_args()
647
647
 
898
898
            webbrowser.open(url, new=True, autoraise=True)
899
899
            sys.exit(0)
900
900
 
901
 
        except Exception, e:
 
901
        except Exception as e:
902
902
            os.write(w, str(e))
903
903
            sys.exit(1)
904
904
 
930
930
                upthread.exc_raise()
931
931
            except KeyboardInterrupt:
932
932
                sys.exit(1)
933
 
            except NeedsCredentials, e:
 
933
            except NeedsCredentials as e:
934
934
                message = _('Please enter your account information for the '
935
935
                            '%s bug tracking system')
936
936
                data = self.ui_question_userpass(message % excstr(e))
941
941
                                                 args=(self.report,
942
942
                                                       progress_callback))
943
943
                    upthread.start()
944
 
            except Exception, e:
 
944
            except Exception as e:
945
945
                self.ui_error_message(_('Network problem'),
946
946
                        '%s\n\n%s' % (
947
947
                            _('Cannot connect to crash database, please check your Internet connection.'),
966
966
            self.report = apport.Report()
967
967
            self.report.load(open(path), binary='compressed')
968
968
            if 'ProblemType' not in self.report:
969
 
                raise ValueError, 'Report does not contain "ProblemType" field'
 
969
                raise ValueError('Report does not contain "ProblemType" field')
970
970
        except MemoryError:
971
971
            self.report = None
972
972
            self.ui_error_message(_('Memory exhaustion'),
973
973
                _('Your system does not have enough memory to process this crash report.'))
974
974
            return False
975
 
        except IOError, e:
 
975
        except IOError as e:
976
976
            self.report = None
977
977
            self.ui_error_message(_('Invalid problem report'), e.strerror)
978
978
            return False
979
 
        except (TypeError, ValueError, zlib.error), e:
 
979
        except (TypeError, ValueError, zlib.error) as e:
980
980
            self.report = None
981
981
            self.ui_error_message(_('Invalid problem report'),
982
982
                '%s\n\n%s' % (
1042
1042
        self.open_url(self.report['BugPatternURL'])
1043
1043
        return True
1044
1044
 
 
1045
    def add_extra_tags(self):
 
1046
        '''Add extra tags to report specified with --tags on CLI.'''
 
1047
 
 
1048
        assert self.report
 
1049
        if self.options.tag:
 
1050
            tags = self.report.get('Tags', '')
 
1051
            if tags:
 
1052
                tags += ' '
 
1053
            self.report['Tags'] = tags + ' '.join(self.options.tag)
 
1054
 
1045
1055
    #
1046
1056
    # abstract UI methods that must be implemented in derived classes
1047
1057
    #
1063
1073
        - Valid values for the 'blacklist' key: True or False (True will cause
1064
1074
          the invocation of report.mark_ignore()).
1065
1075
        '''
1066
 
        raise NotImplementedError, 'this function must be overridden by subclasses'
 
1076
        raise NotImplementedError('this function must be overridden by subclasses')
1067
1077
 
1068
1078
    def ui_present_package_error(self, desktopentry):
1069
1079
        '''Ask what to do with a package failure.
1074
1084
        Return the action: ignore ('cancel'), or report a bug about the problem
1075
1085
        ('report').
1076
1086
        '''
1077
 
        raise NotImplementedError, 'this function must be overridden by subclasses'
 
1087
        raise NotImplementedError('this function must be overridden by subclasses')
1078
1088
 
1079
1089
    def ui_present_kernel_error(self, desktopentry):
1080
1090
        '''Ask what to do with a kernel error.
1085
1095
        Return the action: ignore ('cancel'), or report a bug about the problem
1086
1096
        ('report').
1087
1097
        '''
1088
 
        raise NotImplementedError, 'this function must be overridden by subclasses'
 
1098
        raise NotImplementedError('this function must be overridden by subclasses')
1089
1099
 
1090
1100
    def ui_present_report_details(self, is_update):
1091
1101
        '''Show details of the bug report.
1102
1112
        Return the action: send full report ('full'), send reduced report
1103
1113
        ('reduced'), or do not send anything ('cancel').
1104
1114
        '''
1105
 
        raise NotImplementedError, 'this function must be overridden by subclasses'
 
1115
        raise NotImplementedError('this function must be overridden by subclasses')
1106
1116
 
1107
1117
    def ui_info_message(self, title, text):
1108
1118
        '''Show an information message box with given title and text.'''
1109
1119
 
1110
 
        raise NotImplementedError, 'this function must be overridden by subclasses'
 
1120
        raise NotImplementedError('this function must be overridden by subclasses')
1111
1121
 
1112
1122
    def ui_error_message(self, title, text):
1113
1123
        '''Show an error message box with given title and text.'''
1114
1124
 
1115
 
        raise NotImplementedError, 'this function must be overridden by subclasses'
 
1125
        raise NotImplementedError('this function must be overridden by subclasses')
1116
1126
 
1117
1127
    def ui_start_info_collection_progress(self):
1118
1128
        '''Open a indefinite progress bar for data collection.
1120
1130
        This tells the user to wait while debug information is being
1121
1131
        collected.
1122
1132
        '''
1123
 
        raise NotImplementedError, 'this function must be overridden by subclasses'
 
1133
        raise NotImplementedError('this function must be overridden by subclasses')
1124
1134
 
1125
1135
    def ui_pulse_info_collection_progress(self):
1126
1136
        '''Advance the data collection progress bar.
1127
1137
 
1128
1138
        This function is called every 100 ms.
1129
1139
        '''
1130
 
        raise NotImplementedError, 'this function must be overridden by subclasses'
 
1140
        raise NotImplementedError('this function must be overridden by subclasses')
1131
1141
 
1132
1142
    def ui_stop_info_collection_progress(self):
1133
1143
        '''Close debug data collection progress window.'''
1134
1144
 
1135
 
        raise NotImplementedError, 'this function must be overridden by subclasses'
 
1145
        raise NotImplementedError('this function must be overridden by subclasses')
1136
1146
 
1137
1147
    def ui_start_upload_progress(self):
1138
1148
        '''Open progress bar for data upload.
1139
1149
 
1140
1150
        This tells the user to wait while debug information is being uploaded.
1141
1151
        '''
1142
 
        raise NotImplementedError, 'this function must be overridden by subclasses'
 
1152
        raise NotImplementedError('this function must be overridden by subclasses')
1143
1153
 
1144
1154
    def ui_set_upload_progress(self, progress):
1145
1155
        '''Update data upload progress bar.
1149
1159
 
1150
1160
        This function is called every 100 ms.
1151
1161
        '''
1152
 
        raise NotImplementedError, 'this function must be overridden by subclasses'
 
1162
        raise NotImplementedError('this function must be overridden by subclasses')
1153
1163
 
1154
1164
    def ui_stop_upload_progress(self):
1155
1165
        '''Close debug data upload progress window.'''
1156
1166
 
1157
 
        raise NotImplementedError, 'this function must be overridden by subclasses'
 
1167
        raise NotImplementedError('this function must be overridden by subclasses')
1158
1168
 
1159
1169
    def ui_shutdown(self):
1160
1170
        '''Called right before terminating the program.
1174
1184
        Return True if the user selected "Yes", False if selected "No" or
1175
1185
        "None" on cancel/dialog closing.
1176
1186
        '''
1177
 
        raise NotImplementedError, 'this function must be overridden by subclasses'
 
1187
        raise NotImplementedError('this function must be overridden by subclasses')
1178
1188
 
1179
1189
    def ui_question_choice(self, text, options, multiple):
1180
1190
        '''Show an question with predefined choices.
1186
1196
        Return list of selected option indexes, or None if the user cancelled.
1187
1197
        If multiple == False, the list will always have one element.
1188
1198
        '''
1189
 
        raise NotImplementedError, 'this function must be overridden by subclasses'
 
1199
        raise NotImplementedError('this function must be overridden by subclasses')
1190
1200
 
1191
1201
    def ui_question_file(self, text):
1192
1202
        '''Show a file selector dialog.
1193
1203
 
1194
1204
        Return path if the user selected a file, or None if cancelled.
1195
1205
        '''
1196
 
        raise NotImplementedError, 'this function must be overridden by subclasses'
 
1206
        raise NotImplementedError('this function must be overridden by subclasses')
1197
1207
 
1198
1208
    def ui_question_userpass(self, message):
1199
1209
        '''Request username and password from user.
1203
1213
 
1204
1214
        Return a tuple (username, password), or None if cancelled.
1205
1215
        '''
1206
 
        raise NotImplementedError, 'this function must be overridden by subclasses'
 
1216
        raise NotImplementedError('this function must be overridden by subclasses')
1207
1217
 
1208
1218
class HookUI:
1209
1219
    '''Interactive functions which can be used in package hooks.
1308
1318
 
1309
1319
if  __name__ == '__main__':
1310
1320
    import unittest, shutil, signal, tempfile, resource
1311
 
    from cStringIO import StringIO
 
1321
    try:
 
1322
        from cStringIO import StringIO
 
1323
    except ImportError:
 
1324
        from io import StringIO
1312
1325
    import apport.report
1313
1326
    import problem_report
1314
1327
    import apport.crashdb_impl.memory
1319
1332
        def __init__(self):
1320
1333
            # use our dummy crashdb
1321
1334
            self.crashdb_conf = tempfile.NamedTemporaryFile()
1322
 
            print >> self.crashdb_conf, '''default = 'testsuite'
 
1335
            self.crashdb_conf.write('''default = 'testsuite'
1323
1336
databases = {
1324
1337
    'testsuite': { 
1325
1338
        'impl': 'memory',
1326
1339
        'bug_pattern_base': None
1327
1340
    }
1328
1341
}
1329
 
'''
 
1342
''')
1330
1343
            self.crashdb_conf.flush()
1331
1344
 
1332
1345
            os.environ['APPORT_CRASHDB_CONF'] = self.crashdb_conf.name
1508
1521
 
1509
1522
            fsize = os.path.getsize(self.report_file.name)
1510
1523
            complete_ratio = float(self.ui.get_complete_size()) / fsize
1511
 
            self.assert_(complete_ratio >= 0.99 and complete_ratio <= 1.01)
 
1524
            self.assertTrue(complete_ratio >= 0.99 and complete_ratio <= 1.01)
1512
1525
                
1513
1526
            rs = self.ui.get_reduced_size()
1514
 
            self.assert_(rs > 1000)
1515
 
            self.assert_(rs < 10000)
 
1527
            self.assertTrue(rs > 1000)
 
1528
            self.assertTrue(rs < 10000)
1516
1529
 
1517
1530
            # now add some information (e. g. from package hooks)
1518
1531
            self.ui.report['ExtraInfo'] = 'A' * 50000
1519
1532
            s = self.ui.get_complete_size()
1520
 
            self.assert_(s >= fsize + 49900)
1521
 
            self.assert_(s < fsize + 60000)
 
1533
            self.assertTrue(s >= fsize + 49900)
 
1534
            self.assertTrue(s < fsize + 60000)
1522
1535
 
1523
1536
            rs = self.ui.get_reduced_size()
1524
 
            self.assert_(rs > 51000)
1525
 
            self.assert_(rs < 60000)
 
1537
            self.assertTrue(rs > 51000)
 
1538
            self.assertTrue(rs < 60000)
1526
1539
 
1527
1540
        def test_get_size_constructed(self):
1528
1541
            '''get_complete_size() and get_reduced_size() for on-the-fly Reports.'''
1531
1544
            self.ui.report['Hello'] = 'World'
1532
1545
 
1533
1546
            s = self.ui.get_complete_size()
1534
 
            self.assert_(s > 5)
1535
 
            self.assert_(s < 100)
 
1547
            self.assertTrue(s > 5)
 
1548
            self.assertTrue(s < 100)
1536
1549
 
1537
1550
            self.assertEqual(s, self.ui.get_reduced_size())
1538
1551
 
1551
1564
            self.update_report_file()
1552
1565
            self.ui.load_report(self.report_file.name)
1553
1566
 
1554
 
            self.assert_(self.ui.report == None)
 
1567
            self.assertTrue(self.ui.report == None)
1555
1568
            self.assertEqual(self.ui.msg_title, _('Invalid problem report'))
1556
1569
            self.assertEqual(self.ui.msg_severity, 'info')
1557
1570
 
1568
1581
            self.report_file.flush()
1569
1582
 
1570
1583
            self.ui.load_report(self.report_file.name)
1571
 
            self.assert_(self.ui.report == None)
 
1584
            self.assertTrue(self.ui.report == None)
1572
1585
            self.assertEqual(self.ui.msg_title, _('Invalid problem report'))
1573
1586
            self.assertEqual(self.ui.msg_severity, 'error')
1574
1587
 
1584
1597
 
1585
1598
            self.ui.restart()
1586
1599
            time.sleep(1) # FIXME: race condition
1587
 
            self.assert_(os.path.exists(p))
1588
 
            self.assert_(not os.path.exists(r))
 
1600
            self.assertTrue(os.path.exists(p))
 
1601
            self.assertTrue(not os.path.exists(r))
1589
1602
            os.unlink(p)
1590
1603
 
1591
1604
            # test with RespawnCommand
1595
1608
 
1596
1609
            self.ui.restart()
1597
1610
            time.sleep(1) # FIXME: race condition
1598
 
            self.assert_(not os.path.exists(p))
1599
 
            self.assert_(os.path.exists(r))
 
1611
            self.assertTrue(not os.path.exists(p))
 
1612
            self.assertTrue(os.path.exists(r))
1600
1613
            os.unlink(r)
1601
1614
 
1602
1615
            # test that invalid command does not make us fall apart
1611
1624
            # report without any information (distro bug)
1612
1625
            self.ui.report = apport.Report()
1613
1626
            self.ui.collect_info()
1614
 
            self.assert_(set(['Date', 'Uname', 'DistroRelease', 'ProblemType']).issubset(
 
1627
            self.assertTrue(set(['Date', 'Uname', 'DistroRelease', 'ProblemType']).issubset(
1615
1628
                set(self.ui.report.keys())))
1616
1629
            self.assertEqual(self.ui.ic_progress_pulses, 0,
1617
1630
                'no progress dialog for distro bug info collection')
1629
1642
            self.ui.report['Fstab'] = ('/etc/fstab', True)
1630
1643
            self.ui.report['CompressedValue'] = problem_report.CompressedValue('Test')
1631
1644
            self.ui.collect_info()
1632
 
            self.assert_(set(['SourcePackage', 'Package', 'ProblemType',
 
1645
            self.assertTrue(set(['SourcePackage', 'Package', 'ProblemType',
1633
1646
                'Uname', 'Dependencies', 'DistroRelease', 'Date',
1634
1647
                'ExecutablePath']).issubset(set(self.ui.report.keys())))
1635
 
            self.assert_(self.ui.ic_progress_pulses > 0,
 
1648
            self.assertTrue(self.ui.ic_progress_pulses > 0,
1636
1649
                'progress dialog for package bug info collection')
1637
1650
            self.assertEqual(self.ui.ic_progress_active, False,
1638
1651
                'progress dialog for package bug info collection finished')
1644
1657
            self.ui.report = apport.Report()
1645
1658
            self.ui.cur_package = 'bash'
1646
1659
            self.ui.collect_info()
1647
 
            self.assert_(set(['SourcePackage', 'Package', 'ProblemType',
 
1660
            self.assertTrue(set(['SourcePackage', 'Package', 'ProblemType',
1648
1661
                'Uname', 'Dependencies', 'DistroRelease',
1649
1662
                'Date']).issubset(set(self.ui.report.keys())))
1650
 
            self.assert_(self.ui.ic_progress_pulses > 0,
 
1663
            self.assertTrue(self.ui.ic_progress_pulses > 0,
1651
1664
                'progress dialog for package bug info collection')
1652
1665
            self.assertEqual(self.ui.ic_progress_active, False,
1653
1666
                'progress dialog for package bug info collection finished')
1704
1717
 
1705
1718
            self.assertEqual(self.ui.msg_severity, None)
1706
1719
            self.assertEqual(self.ui.msg_title, None)
1707
 
            self.assert_(self.ui.present_details_shown)
 
1720
            self.assertTrue(self.ui.present_details_shown)
1708
1721
            self.assertEqual(self.ui.opened_url, 'http://bash.bugs.example.com/%i' % self.ui.crashdb.latest_id())
1709
1722
 
1710
 
            self.assert_(self.ui.ic_progress_pulses > 0)
 
1723
            self.assertTrue(self.ui.ic_progress_pulses > 0)
1711
1724
            self.assertEqual(self.ui.report['SourcePackage'], 'bash')
1712
 
            self.assert_('Dependencies' in self.ui.report.keys())
1713
 
            self.assert_('ProcEnviron' in self.ui.report.keys())
 
1725
            self.assertTrue('Dependencies' in self.ui.report.keys())
 
1726
            self.assertTrue('ProcEnviron' in self.ui.report.keys())
1714
1727
            self.assertEqual(self.ui.report['ProblemType'], 'Bug')
1715
1728
 
1716
1729
            # should not crash on nonexisting package
1720
1733
 
1721
1734
            self.assertEqual(self.ui.msg_severity, 'error')
1722
1735
 
1723
 
        def test_run_report_bug_pid(self):
1724
 
            '''run_report_bug() for a pid.'''
 
1736
        def test_run_report_bug_pid_tags(self):
 
1737
            '''run_report_bug() for a pid with extra tags.'''
1725
1738
 
1726
1739
            # fork a test process
1727
1740
            pid = os.fork()
1733
1746
 
1734
1747
            try:
1735
1748
                # report a bug on cat process
1736
 
                sys.argv = ['ui-test', '-f', '-P', str(pid)]
 
1749
                sys.argv = ['ui-test', '-f', '--tag', 'foo', '-P', str(pid)]
1737
1750
                self.ui = _TestSuiteUserInterface()
1738
1751
                self.assertEqual(self.ui.run_argv(), True)
1739
1752
            finally:
1741
1754
                os.kill(pid, signal.SIGKILL)
1742
1755
                os.waitpid(pid, 0)
1743
1756
 
1744
 
            self.assert_('SourcePackage' in self.ui.report.keys())
1745
 
            self.assert_('Dependencies' in self.ui.report.keys())
1746
 
            self.assert_('ProcMaps' in self.ui.report.keys())
 
1757
            self.assertTrue('SourcePackage' in self.ui.report.keys())
 
1758
            self.assertTrue('Dependencies' in self.ui.report.keys())
 
1759
            self.assertTrue('ProcMaps' in self.ui.report.keys())
1747
1760
            self.assertEqual(self.ui.report['ExecutablePath'], '/bin/sleep')
1748
1761
            self.failIf(self.ui.report.has_key('ProcCmdline')) # privacy!
1749
 
            self.assert_('ProcEnviron' in self.ui.report.keys())
 
1762
            self.assertTrue('ProcEnviron' in self.ui.report.keys())
1750
1763
            self.assertEqual(self.ui.report['ProblemType'], 'Bug')
 
1764
            self.assertTrue('Tags' in self.ui.report.keys())
 
1765
            self.assertTrue('foo' in self.ui.report['Tags'])
1751
1766
 
1752
1767
            self.assertEqual(self.ui.msg_severity, None)
1753
1768
            self.assertEqual(self.ui.msg_title, None)
1754
1769
            self.assertEqual(self.ui.opened_url, 'http://coreutils.bugs.example.com/%i' % self.ui.crashdb.latest_id())
1755
 
            self.assert_(self.ui.present_details_shown)
1756
 
            self.assert_(self.ui.ic_progress_pulses > 0)
 
1770
            self.assertTrue(self.ui.present_details_shown)
 
1771
            self.assertTrue(self.ui.ic_progress_pulses > 0)
1757
1772
 
1758
1773
        @classmethod
1759
1774
        def _find_unused_pid(klass):
1764
1779
                pid += 1
1765
1780
                try:
1766
1781
                    os.kill(pid, 0)
1767
 
                except OSError, e:
 
1782
                except OSError as e:
1768
1783
                    if e.errno == errno.ESRCH:
1769
1784
                        break
1770
1785
            return pid
1797
1812
            (fd, exename) = tempfile.mkstemp()
1798
1813
            os.write(fd, open('/bin/cat').read())
1799
1814
            os.close(fd)
1800
 
            os.chmod(exename, 0755)
 
1815
            os.chmod(exename, 0o755)
1801
1816
 
1802
1817
            # unpackaged test process
1803
1818
            pid = os.fork()
1831
1846
            self.assertEqual(self.ui.opened_url, None)
1832
1847
            self.failIf(self.ui.present_details_shown)
1833
1848
 
1834
 
            self.assert_(self.ui.ic_progress_pulses > 0)
 
1849
            self.assertTrue(self.ui.ic_progress_pulses > 0)
1835
1850
 
1836
1851
            r = apport.Report()
1837
1852
            r.load(open(reportfile))
1838
1853
 
1839
1854
            self.assertEqual(r['SourcePackage'], 'bash')
1840
 
            self.assert_('Dependencies' in r.keys())
1841
 
            self.assert_('ProcEnviron' in r.keys())
 
1855
            self.assertTrue('Dependencies' in r.keys())
 
1856
            self.assertTrue('ProcEnviron' in r.keys())
1842
1857
            self.assertEqual(r['ProblemType'], 'Bug')
1843
1858
 
1844
1859
            # report it
1850
1865
 
1851
1866
            self.assertEqual(self.ui.msg_text, None)
1852
1867
            self.assertEqual(self.ui.msg_severity, None)
1853
 
            self.assert_(self.ui.present_details_shown)
 
1868
            self.assertTrue(self.ui.present_details_shown)
1854
1869
 
1855
1870
        def _gen_test_crash(self):
1856
1871
            '''Generate a Report with real crash data.'''
1913
1928
            self.assertEqual(self.ui.msg_title, None)
1914
1929
            self.assertEqual(self.ui.opened_url, None)
1915
1930
            self.assertNotEqual(self.ui.ic_progress_pulses, 0)
1916
 
            self.assert_(self.ui.present_details_shown)
 
1931
            self.assertTrue(self.ui.present_details_shown)
1917
1932
 
1918
1933
            # report in crash notification dialog, send full report
1919
1934
            r.write(open(report_file, 'w'))
1925
1940
            self.assertEqual(self.ui.msg_title, None)
1926
1941
            self.assertEqual(self.ui.opened_url, 'http://coreutils.bugs.example.com/%i' % self.ui.crashdb.latest_id())
1927
1942
            self.assertNotEqual(self.ui.ic_progress_pulses, 0)
1928
 
            self.assert_(self.ui.present_details_shown)
 
1943
            self.assertTrue(self.ui.present_details_shown)
1929
1944
 
1930
 
            self.assert_('SourcePackage' in self.ui.report.keys())
1931
 
            self.assert_('Dependencies' in self.ui.report.keys())
1932
 
            self.assert_('Stacktrace' in self.ui.report.keys())
1933
 
            self.assert_('ProcEnviron' in self.ui.report.keys())
 
1945
            self.assertTrue('SourcePackage' in self.ui.report.keys())
 
1946
            self.assertTrue('Dependencies' in self.ui.report.keys())
 
1947
            self.assertTrue('Stacktrace' in self.ui.report.keys())
 
1948
            self.assertTrue('ProcEnviron' in self.ui.report.keys())
1934
1949
            self.assertEqual(self.ui.report['ProblemType'], 'Crash')
1935
 
            self.assert_(len(self.ui.report['CoreDump']) > 10000)
1936
 
            self.assert_(self.ui.report['Title'].startswith('cat crashed with SIGSEGV'))
 
1950
            self.assertTrue(len(self.ui.report['CoreDump']) > 10000)
 
1951
            self.assertTrue(self.ui.report['Title'].startswith('cat crashed with SIGSEGV'))
1937
1952
 
1938
1953
            # report in crash notification dialog, send reduced report
1939
1954
            r.write(open(report_file, 'w'))
1945
1960
            self.assertEqual(self.ui.msg_title, None)
1946
1961
            self.assertEqual(self.ui.opened_url, 'http://coreutils.bugs.example.com/%i' % self.ui.crashdb.latest_id())
1947
1962
            self.assertNotEqual(self.ui.ic_progress_pulses, 0)
1948
 
            self.assert_(self.ui.present_details_shown)
 
1963
            self.assertTrue(self.ui.present_details_shown)
1949
1964
 
1950
 
            self.assert_('SourcePackage' in self.ui.report.keys())
1951
 
            self.assert_('Dependencies' in self.ui.report.keys())
1952
 
            self.assert_('Stacktrace' in self.ui.report.keys())
 
1965
            self.assertTrue('SourcePackage' in self.ui.report.keys())
 
1966
            self.assertTrue('Dependencies' in self.ui.report.keys())
 
1967
            self.assertTrue('Stacktrace' in self.ui.report.keys())
1953
1968
            self.assertEqual(self.ui.report['ProblemType'], 'Crash')
1954
 
            self.assert_(not self.ui.report.has_key('CoreDump'))
 
1969
            self.assertTrue(not self.ui.report.has_key('CoreDump'))
1955
1970
 
1956
1971
            # so far we did not blacklist, verify that
1957
 
            self.assert_(not self.ui.report.check_ignored())
 
1972
            self.assertTrue(not self.ui.report.check_ignored())
1958
1973
 
1959
1974
            # cancel crash notification dialog and blacklist
1960
1975
            r.write(open(report_file, 'w'))
1966
1981
            self.assertEqual(self.ui.opened_url, None)
1967
1982
            self.assertEqual(self.ui.ic_progress_pulses, 0)
1968
1983
 
1969
 
            self.assert_(self.ui.report.check_ignored())
 
1984
            self.assertTrue(self.ui.report.check_ignored())
1970
1985
 
1971
1986
        def test_run_crash_abort(self):
1972
1987
            '''run_crash() for an unreportable abort()'''
1979
1994
            self.ui.present_details_response = 'full'
1980
1995
            self.ui.run_crash(self.report_file.name)
1981
1996
 
1982
 
            self.assert_('assert' in self.ui.msg_text, '%s: %s' %
 
1997
            self.assertTrue('assert' in self.ui.msg_text, '%s: %s' %
1983
1998
                (self.ui.msg_title, self.ui.msg_text))
1984
1999
            self.assertEqual(self.ui.msg_severity, 'info')
1985
2000
            self.failIf(self.ui.present_details_shown)
1999
2014
 
2000
2015
            self.assertEqual(self.ui.msg_text, None)
2001
2016
            self.assertEqual(self.ui.msg_severity, None)
2002
 
            self.assert_(self.ui.present_details_shown)
 
2017
            self.assertTrue(self.ui.present_details_shown)
2003
2018
 
2004
2019
            # unreportable
2005
2020
            self.report['Package'] = 'bash'
2010
2025
            self.ui = _TestSuiteUserInterface()
2011
2026
            self.assertEqual(self.ui.run_argv(), True)
2012
2027
 
2013
 
            self.assert_('It stinks.' in self.ui.msg_text, '%s: %s' %
 
2028
            self.assertTrue('It stinks.' in self.ui.msg_text, '%s: %s' %
2014
2029
                (self.ui.msg_title, self.ui.msg_text))
2015
2030
            self.assertEqual(self.ui.msg_severity, 'info')
2016
2031
            self.failIf(self.ui.present_details_shown)
2034
2049
 
2035
2050
            self.ui.run_crash(self.report_file.name)
2036
2051
 
2037
 
            self.assert_('It stinks.' in self.ui.msg_text, '%s: %s' %
 
2052
            self.assertTrue('It stinks.' in self.ui.msg_text, '%s: %s' %
2038
2053
                (self.ui.msg_title, self.ui.msg_text))
2039
2054
            self.assertEqual(self.ui.msg_severity, 'info')
2040
2055
 
2082
2097
            self.ui = _TestSuiteUserInterface()
2083
2098
            self.ui.run_crash(report_file)
2084
2099
            self.assertEqual(self.ui.msg_severity, 'error')
2085
 
            self.assert_('memory' in self.ui.msg_text, '%s: %s' %
 
2100
            self.assertTrue('memory' in self.ui.msg_text, '%s: %s' %
2086
2101
                (self.ui.msg_title, self.ui.msg_text))
2087
2102
 
2088
2103
        def test_run_crash_preretraced(self):
2111
2126
            self.assertEqual(self.ui.msg_title, None)
2112
2127
            self.assertEqual(self.ui.opened_url, None)
2113
2128
            self.assertEqual(self.ui.ic_progress_pulses, 0)
2114
 
            self.assert_(self.ui.present_details_shown)
 
2129
            self.assertTrue(self.ui.present_details_shown)
2115
2130
           
2116
2131
        def test_run_crash_errors(self):
2117
2132
            '''run_crash() on various error conditions.'''
2201
2216
            self.assertEqual(self.ui.msg_severity, None)
2202
2217
            self.assertEqual(self.ui.msg_title, None)
2203
2218
            self.assertEqual(self.ui.opened_url, 'http://bash.bugs.example.com/%i' % self.ui.crashdb.latest_id())
2204
 
            self.assert_(self.ui.present_details_shown)
 
2219
            self.assertTrue(self.ui.present_details_shown)
2205
2220
 
2206
 
            self.assert_('SourcePackage' in self.ui.report.keys())
2207
 
            self.assert_('Package' in self.ui.report.keys())
 
2221
            self.assertTrue('SourcePackage' in self.ui.report.keys())
 
2222
            self.assertTrue('Package' in self.ui.report.keys())
2208
2223
            self.assertEqual(self.ui.report['ProblemType'], 'Package')
2209
2224
 
2210
2225
            # verify that additional information has been collected
2211
 
            self.assert_('Architecture' in self.ui.report.keys())
2212
 
            self.assert_('DistroRelease' in self.ui.report.keys())
2213
 
            self.assert_('Uname' in self.ui.report.keys())
 
2226
            self.assertTrue('Architecture' in self.ui.report.keys())
 
2227
            self.assertTrue('DistroRelease' in self.ui.report.keys())
 
2228
            self.assertTrue('Uname' in self.ui.report.keys())
2214
2229
 
2215
2230
        def test_run_crash_kernel(self):
2216
2231
            '''run_crash() for a kernel error.'''
2252
2267
                ' ' + str(self.ui.msg_text))
2253
2268
            self.assertEqual(self.ui.msg_title, None)
2254
2269
            self.assertEqual(self.ui.opened_url, 'http://linux.bugs.example.com/%i' % self.ui.crashdb.latest_id())
2255
 
            self.assert_(self.ui.present_details_shown)
 
2270
            self.assertTrue(self.ui.present_details_shown)
2256
2271
 
2257
 
            self.assert_('SourcePackage' in self.ui.report.keys())
 
2272
            self.assertTrue('SourcePackage' in self.ui.report.keys())
2258
2273
            # did we run the hooks properly?
2259
 
            self.assert_('KernelDebug' in self.ui.report.keys())
 
2274
            self.assertTrue('KernelDebug' in self.ui.report.keys())
2260
2275
            self.assertEqual(self.ui.report['ProblemType'], 'KernelCrash')
2261
2276
 
2262
2277
        def test_run_crash_anonymity(self):
2290
2305
                    '', {'dummy_data': 1})
2291
2306
 
2292
2307
            self.assertEqual(self.ui.run_argv(), False)
2293
 
            self.assert_('No additional information collected.' in
 
2308
            self.assertTrue('No additional information collected.' in
2294
2309
                    self.ui.msg_text)
2295
2310
            self.failIf(self.ui.present_details_shown)
2296
2311
 
2303
2318
                    '', {'dummy_data': 1})
2304
2319
 
2305
2320
            self.assertEqual(self.ui.run_argv(), False)
2306
 
            self.assert_('No additional information collected.' in
 
2321
            self.assertTrue('No additional information collected.' in
2307
2322
                    self.ui.msg_text)
2308
2323
            self.failIf(self.ui.present_details_shown)
2309
2324
 
2321
2336
            self.assertEqual(self.ui.msg_severity, None, self.ui.msg_text)
2322
2337
            self.assertEqual(self.ui.msg_title, None)
2323
2338
            self.assertEqual(self.ui.opened_url, None)
2324
 
            self.assert_(self.ui.present_details_shown)
2325
 
 
2326
 
            self.assert_(self.ui.ic_progress_pulses > 0)
2327
 
            self.assert_(self.ui.report['Package'].startswith('bash '))
2328
 
            self.assert_('Dependencies' in self.ui.report.keys())
2329
 
            self.assert_('ProcEnviron' in self.ui.report.keys())
2330
 
 
2331
 
        def test_run_update_report_existing_package_cli(self):
2332
 
            '''run_update_report() on an existing package (CLI argument).'''
2333
 
 
2334
 
            sys.argv = ['ui-test', '-u', '1', '-p', 'bash']
 
2339
            self.assertTrue(self.ui.present_details_shown)
 
2340
 
 
2341
            self.assertTrue(self.ui.ic_progress_pulses > 0)
 
2342
            self.assertTrue(self.ui.report['Package'].startswith('bash '))
 
2343
            self.assertTrue('Dependencies' in self.ui.report.keys())
 
2344
            self.assertTrue('ProcEnviron' in self.ui.report.keys())
 
2345
 
 
2346
        def test_run_update_report_existing_package_cli_tags(self):
 
2347
            '''run_update_report() on an existing package (CLI argument) with extra tag'''
 
2348
 
 
2349
            sys.argv = ['ui-test', '-u', '1', '-p', 'bash', '--tag', 'foo']
2335
2350
            self.ui = _TestSuiteUserInterface()
2336
2351
            self.ui.crashdb = apport.crashdb_impl.memory.CrashDatabase(None,
2337
2352
                    '', {'dummy_data': 1})
2340
2355
            self.assertEqual(self.ui.msg_severity, None, self.ui.msg_text)
2341
2356
            self.assertEqual(self.ui.msg_title, None)
2342
2357
            self.assertEqual(self.ui.opened_url, None)
2343
 
            self.assert_(self.ui.present_details_shown)
 
2358
            self.assertTrue(self.ui.present_details_shown)
2344
2359
 
2345
 
            self.assert_(self.ui.ic_progress_pulses > 0)
2346
 
            self.assert_(self.ui.report['Package'].startswith('bash '))
2347
 
            self.assert_('Dependencies' in self.ui.report.keys())
2348
 
            self.assert_('ProcEnviron' in self.ui.report.keys())
 
2360
            self.assertTrue(self.ui.ic_progress_pulses > 0)
 
2361
            self.assertTrue(self.ui.report['Package'].startswith('bash '))
 
2362
            self.assertTrue('Dependencies' in self.ui.report.keys())
 
2363
            self.assertTrue('ProcEnviron' in self.ui.report.keys())
 
2364
            self.assertTrue('foo' in self.ui.report['Tags'])
2349
2365
 
2350
2366
        def test_run_update_report_existing_package_cli_cmdname(self):
2351
2367
            '''run_update_report() on an existing package (-collect program).'''
2359
2375
            self.assertEqual(self.ui.msg_severity, None, self.ui.msg_text)
2360
2376
            self.assertEqual(self.ui.msg_title, None)
2361
2377
            self.assertEqual(self.ui.opened_url, None)
2362
 
            self.assert_(self.ui.present_details_shown)
 
2378
            self.assertTrue(self.ui.present_details_shown)
2363
2379
 
2364
 
            self.assert_(self.ui.ic_progress_pulses > 0)
2365
 
            self.assert_(self.ui.report['Package'].startswith('bash '))
2366
 
            self.assert_('Dependencies' in self.ui.report.keys())
2367
 
            self.assert_('ProcEnviron' in self.ui.report.keys())
 
2380
            self.assertTrue(self.ui.ic_progress_pulses > 0)
 
2381
            self.assertTrue(self.ui.report['Package'].startswith('bash '))
 
2382
            self.assertTrue('Dependencies' in self.ui.report.keys())
 
2383
            self.assertTrue('ProcEnviron' in self.ui.report.keys())
2368
2384
 
2369
2385
        def test_run_update_report_noninstalled_but_hook(self):
2370
2386
            '''run_update_report() on an uninstalled package with a source hook.'''
2382
2398
            self.assertEqual(self.ui.msg_severity, None, self.ui.msg_text)
2383
2399
            self.assertEqual(self.ui.msg_title, None)
2384
2400
            self.assertEqual(self.ui.opened_url, None)
2385
 
            self.assert_(self.ui.present_details_shown)
 
2401
            self.assertTrue(self.ui.present_details_shown)
2386
2402
 
2387
 
            self.assert_(self.ui.ic_progress_pulses > 0)
 
2403
            self.assertTrue(self.ui.ic_progress_pulses > 0)
2388
2404
            self.assertEqual(self.ui.report['Package'], 'foo (not installed)')
2389
2405
            self.assertEqual(self.ui.report['MachineType'], 'Laptop')
2390
 
            self.assert_('ProcEnviron' in self.ui.report.keys())
 
2406
            self.assertTrue('ProcEnviron' in self.ui.report.keys())
2391
2407
 
2392
2408
        def _run_hook(self, code):
2393
2409
            f = open(os.path.join(self.hookdir, 'coreutils.py'), 'w')
2483
2499
            sys.argv = ['ui-test', '-s', 'foobar' ]
2484
2500
            self.ui = _TestSuiteUserInterface()
2485
2501
            self.assertEqual(self.ui.run_argv(), True)
2486
 
            self.assert_('foobar" is not known' in self.ui.msg_text)
 
2502
            self.assertTrue('foobar" is not known' in self.ui.msg_text)
2487
2503
            self.assertEqual(self.ui.msg_severity, 'error')
2488
2504
 
2489
2505
            # does not determine package
2490
2506
            f = open(os.path.join(symptom_script_dir, 'nopkg.py'), 'w')
2491
 
            print >> f, 'def run(report, ui):\n    pass'
 
2507
            f.write('def run(report, ui):\n    pass\n')
2492
2508
            f.close()
2493
2509
            orig_stderr = sys.stderr
2494
2510
            sys.argv = ['ui-test', '-s', 'nopkg' ]
2497
2513
            self.assertRaises(SystemExit, self.ui.run_argv)
2498
2514
            err = sys.stderr.getvalue()
2499
2515
            sys.stderr = orig_stderr
2500
 
            self.assert_('did not determine the affected package' in err)
 
2516
            self.assertTrue('did not determine the affected package' in err)
2501
2517
 
2502
2518
            # does not define run()
2503
2519
            f = open(os.path.join(symptom_script_dir, 'norun.py'), 'w')
2504
 
            print >> f, 'def something(x, y):\n    return 1'
 
2520
            f.write('def something(x, y):\n    return 1\n')
2505
2521
            f.close()
2506
2522
            sys.argv = ['ui-test', '-s', 'norun' ]
2507
2523
            self.ui = _TestSuiteUserInterface()
2509
2525
            self.assertRaises(SystemExit, self.ui.run_argv)
2510
2526
            err = sys.stderr.getvalue()
2511
2527
            sys.stderr = orig_stderr
2512
 
            self.assert_('norun.py crashed:' in err)
 
2528
            self.assertTrue('norun.py crashed:' in err)
2513
2529
 
2514
2530
            # crashing script
2515
2531
            f = open(os.path.join(symptom_script_dir, 'crash.py'), 'w')
2516
 
            print >> f, 'def run(report, ui):\n    return 1/0'
 
2532
            f.write('def run(report, ui):\n    return 1/0\n')
2517
2533
            f.close()
2518
2534
            sys.argv = ['ui-test', '-s', 'crash' ]
2519
2535
            self.ui = _TestSuiteUserInterface()
2521
2537
            self.assertRaises(SystemExit, self.ui.run_argv)
2522
2538
            err = sys.stderr.getvalue()
2523
2539
            sys.stderr = orig_stderr
2524
 
            self.assert_('crash.py crashed:' in err)
2525
 
            self.assert_('ZeroDivisionError:' in err)
 
2540
            self.assertTrue('crash.py crashed:' in err)
 
2541
            self.assertTrue('ZeroDivisionError:' in err)
2526
2542
 
2527
2543
            # working noninteractive script
2528
2544
            f = open(os.path.join(symptom_script_dir, 'itching.py'), 'w')
2529
 
            print >> f, 'def run(report, ui):\n  report["itch"] = "scratch"\n  return "bash"'
 
2545
            f.write('def run(report, ui):\n  report["itch"] = "scratch"\n  return "bash"\n')
2530
2546
            f.close()
2531
2547
            sys.argv = ['ui-test', '-s', 'itching' ]
2532
2548
            self.ui = _TestSuiteUserInterface()
2533
2549
            self.assertEqual(self.ui.run_argv(), True)
2534
2550
            self.assertEqual(self.ui.msg_text, None)
2535
2551
            self.assertEqual(self.ui.msg_severity, None)
2536
 
            self.assert_(self.ui.present_details_shown)
 
2552
            self.assertTrue(self.ui.present_details_shown)
2537
2553
 
2538
2554
            self.assertEqual(self.ui.report['itch'], 'scratch')
2539
 
            self.assert_('DistroRelease' in self.ui.report)
 
2555
            self.assertTrue('DistroRelease' in self.ui.report)
2540
2556
            self.assertEqual(self.ui.report['SourcePackage'], 'bash')
2541
 
            self.assert_(self.ui.report['Package'].startswith('bash '))
 
2557
            self.assertTrue(self.ui.report['Package'].startswith('bash '))
2542
2558
            self.assertEqual(self.ui.report['ProblemType'], 'Bug')
2543
2559
 
 
2560
            # working noninteractive script with extra tag
 
2561
            sys.argv = ['ui-test', '--tag', 'foo', '-s', 'itching' ]
 
2562
            self.ui = _TestSuiteUserInterface()
 
2563
            self.assertEqual(self.ui.run_argv(), True)
 
2564
            self.assertEqual(self.ui.msg_text, None)
 
2565
            self.assertEqual(self.ui.msg_severity, None)
 
2566
            self.assertTrue(self.ui.present_details_shown)
 
2567
 
 
2568
            self.assertEqual(self.ui.report['itch'], 'scratch')
 
2569
            self.assertTrue('foo' in self.ui.report['Tags'])
 
2570
 
2544
2571
            # working interactive script
2545
2572
            f = open(os.path.join(symptom_script_dir, 'itching.py'), 'w')
2546
 
            print >> f, '''def run(report, ui):
 
2573
            f.write('''def run(report, ui):
2547
2574
    report['itch'] = 'slap'
2548
2575
    report['q'] = str(ui.yesno('do you?'))
2549
2576
    return 'bash'
2550
 
'''
 
2577
''')
2551
2578
            f.close()
2552
2579
            sys.argv = ['ui-test', '-s', 'itching' ]
2553
2580
            self.ui = _TestSuiteUserInterface()
2554
2581
            self.ui.question_yesno_response = True
2555
2582
            self.assertEqual(self.ui.run_argv(), True)
2556
 
            self.assert_(self.ui.present_details_shown)
 
2583
            self.assertTrue(self.ui.present_details_shown)
2557
2584
            self.assertEqual(self.ui.msg_text, 'do you?')
2558
2585
 
2559
2586
            self.assertEqual(self.ui.report['itch'], 'slap')
2560
 
            self.assert_('DistroRelease' in self.ui.report)
 
2587
            self.assertTrue('DistroRelease' in self.ui.report)
2561
2588
            self.assertEqual(self.ui.report['SourcePackage'], 'bash')
2562
 
            self.assert_(self.ui.report['Package'].startswith('bash '))
 
2589
            self.assertTrue(self.ui.report['Package'].startswith('bash '))
2563
2590
            self.assertEqual(self.ui.report['ProblemType'], 'Bug')
2564
2591
            self.assertEqual(self.ui.report['q'], 'True')
2565
2592
 
2567
2594
            '''run_report_bug() without specifying arguments and available symptoms.'''
2568
2595
 
2569
2596
            f = open(os.path.join(symptom_script_dir, 'foo.py'), 'w')
2570
 
            print >> f, '''description = 'foo does not work'
 
2597
            f.write('''description = 'foo does not work'
2571
2598
def run(report, ui):
2572
2599
    return 'bash'
2573
 
'''
 
2600
''')
2574
2601
            f.close()
2575
2602
            f = open(os.path.join(symptom_script_dir, 'bar.py'), 'w')
2576
 
            print >> f, 'def run(report, ui):\n  return "coreutils"'
 
2603
            f.write('def run(report, ui):\n  return "coreutils"\n')
2577
2604
            f.close()
2578
2605
 
2579
2606
            sys.argv = ['ui-test', '-f']
2582
2609
            self.ui.question_choice_response = None
2583
2610
            self.assertEqual(self.ui.run_argv(), True)
2584
2611
            self.assertEqual(self.ui.msg_severity, None)
2585
 
            self.assert_('kind of problem' in self.ui.msg_text)
 
2612
            self.assertTrue('kind of problem' in self.ui.msg_text)
2586
2613
            self.assertEqual(set(self.ui.msg_choices), 
2587
2614
                    set(['bar', 'foo does not work', 'Other problem']))
2588
2615
 
2595
2622
            self.ui.question_choice_response = [self.ui.msg_choices.index('foo does not work')]
2596
2623
            self.assertEqual(self.ui.run_argv(), True)
2597
2624
            self.assertEqual(self.ui.msg_severity, None)
2598
 
            self.assert_(self.ui.ic_progress_pulses > 0)
2599
 
            self.assert_(self.ui.present_details_shown)
2600
 
            self.assert_(self.ui.report['Package'].startswith('bash'))
 
2625
            self.assertTrue(self.ui.ic_progress_pulses > 0)
 
2626
            self.assertTrue(self.ui.present_details_shown)
 
2627
            self.assertTrue(self.ui.report['Package'].startswith('bash'))
2601
2628
 
2602
 
        def test_parse_argv(self):
 
2629
        def test_parse_argv_single_arg(self):
2603
2630
            '''parse_args() option inference for a single argument'''
2604
2631
 
2605
 
            def _chk(program_name, arg, expected_opts, argv_options=None):
 
2632
            def _chk(program_name, arg, expected_opts):
2606
2633
                sys.argv = [program_name]
2607
 
                if argv_options:
2608
 
                    sys.argv += argv_options
2609
2634
                if arg:
2610
2635
                    sys.argv.append(arg)
2611
2636
                orig_stderr = sys.stderr
2622
2647
            # no arguments -> show pending crashes
2623
2648
            _chk('apport-gtk', None, {'filebug': False, 'package': None,
2624
2649
                'pid': None, 'crash_file': None, 'symptom': None, 
2625
 
                'update_report': None, 'save': None})
2626
 
            # ... except when being called as '*-bug', then default to bug mode
2627
 
            _chk('apport-bug', None, {'filebug': True, 'package': None,
2628
 
                'pid': None, 'crash_file': None, 'symptom': None, 
2629
 
                'update_report': None, 'save': None})
 
2650
                'update_report': None, 'save': None, 'tag': []})
2630
2651
            # updating report not allowed without args
2631
2652
            self.assertRaises(SystemExit, _chk, 'apport-collect', None, {})
2632
2653
 
2633
2654
            # package 
2634
2655
            _chk('apport-kde', 'coreutils', {'filebug': True, 'package':
2635
2656
                'coreutils', 'pid': None, 'crash_file': None, 'symptom': None, 
2636
 
                'update_report': None, 'save': None})
2637
 
            _chk('apport-bug', 'coreutils', {'filebug': True, 'package':
2638
 
                'coreutils', 'pid': None, 'crash_file': None, 'symptom': None, 
2639
 
                'update_report': None, 'save': None})
2640
 
            _chk('apport-bug', 'coreutils', {'filebug': True, 'package':
2641
 
                'coreutils', 'pid': None, 'crash_file': None, 'symptom': None, 
2642
 
                'update_report': None, 'save': 'foo.apport'}, 
2643
 
                ['--save', 'foo.apport'])
 
2657
                'update_report': None, 'save': None, 'tag': []})
2644
2658
 
2645
2659
            # symptom is preferred over package
2646
2660
            f = open(os.path.join(symptom_script_dir, 'coreutils.py'), 'w')
2647
 
            print >> f, '''description = 'foo does not work'
 
2661
            f.write('''description = 'foo does not work'
2648
2662
def run(report, ui):
2649
2663
    return 'bash'
2650
 
'''
 
2664
''')
2651
2665
            f.close()
2652
2666
            _chk('apport-cli', 'coreutils', {'filebug': True, 'package': None,
2653
2667
                 'pid': None, 'crash_file': None, 'symptom': 'coreutils',
2654
 
                 'update_report': None, 'save': None})
2655
 
            _chk('apport-bug', 'coreutils', {'filebug': True, 'package': None,
2656
 
                 'pid': None, 'crash_file': None, 'symptom': 'coreutils',
2657
 
                 'update_report': None, 'save': None})
 
2668
                 'update_report': None, 'save': None, 'tag': []})
2658
2669
 
2659
2670
            # PID
2660
2671
            _chk('apport-cli', '1234', {'filebug': True, 'package': None,
2661
2672
                 'pid': '1234', 'crash_file': None, 'symptom': None,
2662
 
                 'update_report': None, 'save': None})
2663
 
            _chk('apport-bug', '1234', {'filebug': True, 'package': None,
2664
 
                 'pid': '1234', 'crash_file': None, 'symptom': None,
2665
 
                 'update_report': None, 'save': None})
 
2673
                 'update_report': None, 'save': None, 'tag': []})
2666
2674
 
2667
2675
            # .crash/.apport files; check correct handling of spaces
2668
2676
            for suffix in ('.crash', '.apport'):
2669
 
                for prog in ('apport-cli', 'apport-bug'):
2670
 
                    _chk(prog, '/tmp/f oo' + suffix, {'filebug': False,
2671
 
                         'package': None, 'pid': None, 
2672
 
                         'crash_file': '/tmp/f oo' + suffix, 'symptom': None,
2673
 
                         'update_report': None, 'save': None})
 
2677
                _chk('apport-cli', '/tmp/f oo' + suffix, {'filebug': False,
 
2678
                     'package': None, 'pid': None, 
 
2679
                     'crash_file': '/tmp/f oo' + suffix, 'symptom': None,
 
2680
                     'update_report': None, 'save': None, 'tag': []})
2674
2681
 
2675
2682
            # executable
2676
2683
            _chk('apport-cli', '/usr/bin/tail', {'filebug': True, 
2677
2684
                 'package': 'coreutils',
2678
2685
                 'pid': None, 'crash_file': None, 'symptom': None, 
2679
 
                 'update_report': None, 'save': None})
2680
 
            _chk('apport-bug', '/usr/bin/tail', {'filebug': True, 
2681
 
                 'package': 'coreutils',
2682
 
                 'pid': None, 'crash_file': None, 'symptom': None, 
2683
 
                 'update_report': None, 'save': None})
 
2686
                 'update_report': None, 'save': None, 'tag': []})
2684
2687
 
2685
2688
            # update existing report
2686
2689
            _chk('apport-collect', '1234', {'filebug': False, 'package': None,
2687
 
                 'crash_file': None, 'symptom': None, 'update_report': 1234})
 
2690
                 'crash_file': None, 'symptom': None, 'update_report': 1234,
 
2691
                 'tag': []})
2688
2692
            _chk('apport-update-bug', '1234', {'filebug': False, 'package': None,
2689
 
                 'crash_file': None, 'symptom': None, 'update_report': 1234})
 
2693
                 'crash_file': None, 'symptom': None, 'update_report': 1234,
 
2694
                 'tag': []})
 
2695
 
 
2696
        def test_parse_argv_apport_bug(self):
 
2697
            '''parse_args() option inference when invoked as *-bug'''
 
2698
 
 
2699
            def _chk(args, expected_opts):
 
2700
                sys.argv = ['apport-bug'] + args
 
2701
                orig_stderr = sys.stderr
 
2702
                sys.stderr = open('/dev/null', 'w')
 
2703
                try:
 
2704
                    ui = UserInterface()
 
2705
                finally:
 
2706
                    sys.stderr.close()
 
2707
                    sys.stderr = orig_stderr
 
2708
                expected_opts['version'] = None
 
2709
                self.assertEqual(ui.args, [])
 
2710
                self.assertEqual(ui.options, expected_opts)
 
2711
 
 
2712
            #
 
2713
            # no arguments: default to 'ask for symptom' bug mode
 
2714
            #
 
2715
            _chk([], {'filebug': True, 'package': None,
 
2716
                'pid': None, 'crash_file': None, 'symptom': None, 
 
2717
                'update_report': None, 'save': None, 'tag': []})
 
2718
 
 
2719
            #
 
2720
            # single arguments
 
2721
            #
 
2722
 
 
2723
            # package
 
2724
            _chk(['coreutils'], {'filebug': True, 'package':
 
2725
                'coreutils', 'pid': None, 'crash_file': None, 'symptom': None, 
 
2726
                'update_report': None, 'save': None, 'tag': []})
 
2727
 
 
2728
            # symptom (preferred over package)
 
2729
            f = open(os.path.join(symptom_script_dir, 'coreutils.py'), 'w')
 
2730
            f.write('''description = 'foo does not work'
 
2731
def run(report, ui):
 
2732
    return 'bash'
 
2733
''')
 
2734
            f.close()
 
2735
            _chk(['coreutils'], {'filebug': True, 'package': None,
 
2736
                 'pid': None, 'crash_file': None, 'symptom': 'coreutils',
 
2737
                 'update_report': None, 'save': None, 'tag': []})
 
2738
            os.unlink(os.path.join(symptom_script_dir, 'coreutils.py'))
 
2739
 
 
2740
            # PID
 
2741
            _chk(['1234'], {'filebug': True, 'package': None,
 
2742
                 'pid': '1234', 'crash_file': None, 'symptom': None,
 
2743
                 'update_report': None, 'save': None, 'tag': []})
 
2744
 
 
2745
            # .crash/.apport files; check correct handling of spaces
 
2746
            for suffix in ('.crash', '.apport'):
 
2747
                _chk(['/tmp/f oo' + suffix], {'filebug': False,
 
2748
                     'package': None, 'pid': None, 
 
2749
                     'crash_file': '/tmp/f oo' + suffix, 'symptom': None,
 
2750
                     'update_report': None, 'save': None, 'tag': []})
 
2751
 
 
2752
            # executable name
 
2753
            _chk(['/usr/bin/tail'], {'filebug': True, 'package': 'coreutils',
 
2754
                 'pid': None, 'crash_file': None, 'symptom': None, 
 
2755
                 'update_report': None, 'save': None, 'tag': []})
 
2756
 
 
2757
            #
 
2758
            # supported options
 
2759
            #
 
2760
 
 
2761
            # --save
 
2762
            _chk(['--save', 'foo.apport', 'coreutils'], {'filebug': True,
 
2763
                'package': 'coreutils', 'pid': None, 'crash_file': None,
 
2764
                'symptom': None, 'update_report': None, 'save': 'foo.apport',
 
2765
                'tag': []})
 
2766
 
 
2767
            # --tag
 
2768
            _chk(['--tag', 'foo', 'coreutils'], {'filebug': True,
 
2769
                'package': 'coreutils', 'pid': None, 'crash_file': None,
 
2770
                'symptom': None, 'update_report': None, 'save': None,
 
2771
                'tag': ['foo']})
 
2772
            _chk(['--tag', 'foo', '--tag', 'bar', 'coreutils'], {
 
2773
                'filebug': True, 'package': 'coreutils', 'pid': None,
 
2774
                'crash_file': None, 'symptom': None, 'update_report': None,
 
2775
                'save': None, 'tag': ['foo', 'bar']})
2690
2776
 
2691
2777
    unittest.main()
2692
2778