~ubuntu-branches/ubuntu/utopic/apport/utopic

« back to all changes in this revision

Viewing changes to test/test_signal_crashes.py

  • Committer: Package Import Robot
  • Author(s): Martin Pitt
  • Date: 2012-07-09 08:14:30 UTC
  • mfrom: (148.1.66)
  • Revision ID: package-import@ubuntu.com-20120709081430-jim1xdcbior31pph
Tags: 2.3-0ubuntu1
* New upstream release:
  - launchpad.py: Rework test suite to not use Launchpad's +storeblob
    facility at all any more. It almost never works on staging and is
    horribly slow. Fake the bug creation from a blob by manually creating
    the comment and attachments ourselves, and just assume that storeblob
    works on production.  Also change the structure to allow running every
    test individually.
  - crash-digger: Add --crash-db option to specify a non-default crash
    database name. (LP: #1003506)
  - apport-gtk: Add --hanging option to specify the process ID of a hanging
    application. If the user chooses to report this error, apport will
    terminate the pid with SIGABRT, otherwise it will send SIGKILL. The
    normal core pipe handler will be used to process the resulting report
    file, with a .hanging file in /var/crash to separate these from regular
    crashes.
  - apport: Also treat a binary as modified if the /proc/pid/exe symlink
    does not point to an existing file any more. (LP: #984944)
  - Fix PEP-8 violations picked up by latest pep8 checker.
  - ui.py: Do not ignore certain exceptions during upload which are not
    likely to be a network error.
  - launchpad.py: Recongize Launchpad projects for bug query and marking
    operations. (LP: #1003506)
  - packaging-apt-dpkg.py: Fix get_source_tree() to work with apt sandboxes.
  - apport-retrace: Turn StacktraceSource generation back on, now that it
    works with the current sandboxing.
  - launchpad.py: Ensure that upload chunk size does not underrun.
    (LP: #1013334)
  - apport_python_hook: Fix UnicodeEncodeError crash with Python 2 for
    exceptions with non-ASCII characters. (LP: #972436)
  - test_ui_kde.py: Fix occasional test failure in test_1_crash_details if
    the application ends before the "is progress bar visible" check is done.

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
test_source = 'coreutils'
17
17
 
18
18
required_fields = ['ProblemType', 'CoreDump', 'Date', 'ExecutablePath',
19
 
    'ProcCmdline', 'ProcEnviron', 'ProcMaps', 'Signal', 'UserGroups']
 
19
                   'ProcCmdline', 'ProcEnviron', 'ProcMaps', 'Signal',
 
20
                   'UserGroups']
20
21
 
21
22
ifpath = os.path.expanduser(apport.report._ignore_file)
22
23
 
40
41
        os.chdir('/tmp')
41
42
 
42
43
        # expected report name for test executable report
43
 
        self.test_report = os.path.join(apport.fileutils.report_dir,
44
 
                '%s.%i.crash' % (test_executable.replace('/', '_'), os.getuid()))
 
44
        self.test_report = os.path.join(
 
45
            apport.fileutils.report_dir, '%s.%i.crash' %
 
46
            (test_executable.replace('/', '_'), os.getuid()))
45
47
 
46
48
    def tearDown(self):
47
49
        shutil.rmtree(self.report_dir)
65
67
        test_proc = self.create_test_process()
66
68
        try:
67
69
            app = subprocess.Popen([apport_path, str(test_proc), '42', '0'],
68
 
                close_fds=True, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
 
70
                                   stdin=subprocess.PIPE, stderr=subprocess.PIPE)
69
71
            app.stdin.close()
70
72
            assert app.wait() == 0, app.stderr.read()
71
73
        finally:
82
84
        # check crash report
83
85
        self.assertEqual(apport.fileutils.get_all_reports(), [self.test_report])
84
86
        st = os.stat(self.test_report)
85
 
        self.assertEqual(stat.S_IMODE(st.st_mode), 0o640,
86
 
                'report has correct permissions')
 
87
        self.assertEqual(stat.S_IMODE(st.st_mode), 0o640, 'report has correct permissions')
87
88
 
88
89
        # a subsequent crash does not alter unseen report
89
90
        self.do_crash()
100
101
        with open(self.test_report, 'rb') as f:
101
102
            pr.load(f)
102
103
        self.assertTrue(set(required_fields).issubset(set(pr.keys())),
103
 
                'report has required fields')
 
104
                        'report has required fields')
104
105
        self.assertEqual(pr['ExecutablePath'], test_executable)
105
106
        self.assertEqual(pr['ProcCmdline'], test_executable)
106
107
        self.assertEqual(pr['Signal'], '%i' % signal.SIGSEGV)
107
108
 
108
109
        # check safe environment subset
109
110
        allowed_vars = ['SHELL', 'PATH', 'LANGUAGE', 'LANG', 'LC_CTYPE',
110
 
            'LC_COLLATE', 'LC_TIME', 'LC_NUMERIC', 'LC_MONETARY', 'LC_MESSAGES',
111
 
            'LC_PAPER', 'LC_NAME', 'LC_ADDRESS', 'LC_TELEPHONE', 'LC_MEASUREMENT',
112
 
            'LC_IDENTIFICATION', 'LOCPATH', 'TERM']
 
111
                        'LC_COLLATE', 'LC_TIME', 'LC_NUMERIC', 'LC_MONETARY',
 
112
                        'LC_MESSAGES', 'LC_PAPER', 'LC_NAME', 'LC_ADDRESS',
 
113
                        'LC_TELEPHONE', 'LC_MEASUREMENT', 'LC_IDENTIFICATION',
 
114
                        'LOCPATH', 'TERM']
113
115
 
114
116
        for l in pr['ProcEnviron'].splitlines():
115
117
            (k, v) = l.split('=', 1)
116
118
            self.assertTrue(k in allowed_vars,
117
 
                    'report contains sensitive environment variable %s' % k)
 
119
                            'report contains sensitive environment variable %s' % k)
118
120
 
119
121
        # UserGroups only has system groups
120
122
        for g in pr['UserGroups'].split():
121
123
            self.assertLess(grp.getgrnam(g).gr_gid, 500)
122
124
 
123
125
        self.assertFalse('root' in pr['UserGroups'],
124
 
                'collected system groups are not those from root')
 
126
                         'collected system groups are not those from root')
125
127
 
126
128
    def test_parallel_crash(self):
127
129
        '''only one apport instance is ran at a time'''
130
132
        test_proc2 = self.create_test_process(False, '/bin/dd')
131
133
        try:
132
134
            app = subprocess.Popen([apport_path, str(test_proc), '42', '0'],
133
 
                close_fds=True, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
 
135
                                   stdin=subprocess.PIPE, stderr=subprocess.PIPE)
134
136
 
135
137
            time.sleep(0.5)  # give it some time to grab the lock
136
138
 
137
139
            app2 = subprocess.Popen([apport_path, str(test_proc2), '42', '0'],
138
 
                close_fds=True, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
 
140
                                    stdin=subprocess.PIPE, stderr=subprocess.PIPE)
139
141
 
140
142
            # app should wait indefinitely for stdin, while app2 should terminate
141
143
            # immediately (give it 5 seconds)
177
179
 
178
180
        try:
179
181
            app = subprocess.Popen([apport_path, str(test_proc), '42', '0'],
180
 
                close_fds=True, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
 
182
                                   stdin=subprocess.PIPE, stderr=subprocess.PIPE)
181
183
            app.stdin.write(b'boo')
182
184
            app.stdin.close()
183
185
 
233
235
        self.do_crash(check_running=False, command=local_exe, sleep=2)
234
236
 
235
237
        leak = os.path.join(apport.fileutils.report_dir, '_usr_bin_perl.%i.crash' %
236
 
            (os.getuid()))
 
238
                            (os.getuid()))
237
239
        pr = apport.Report()
238
240
        with open(leak, 'rb') as f:
239
241
            pr.load(f)
363
365
        test_proc = self.create_test_process()
364
366
        try:
365
367
            app = subprocess.Popen([apport_path, str(test_proc), '42', '0'],
366
 
                close_fds=True, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
 
368
                                   stdin=subprocess.PIPE, stderr=subprocess.PIPE)
367
369
            # pipe an entire total memory size worth of spaces into it, which must be
368
370
            # bigger than the 'usable' memory size. apport should digest that and the
369
371
            # report should not have a core dump; NB that this should error out
441
443
                os.utime(myexe, None)
442
444
 
443
445
                app = subprocess.Popen([apport_path, str(test_proc), '42', '0'],
444
 
                    close_fds=True, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
 
446
                                       stdin=subprocess.PIPE, stderr=subprocess.PIPE)
445
447
                app.stdin.write(b'boo')
446
448
                app.stdin.close()
447
449
                err = app.stderr.read().decode()
463
465
            env = os.environ.copy()
464
466
            env['APPORT_LOG_FILE'] = log
465
467
            app = subprocess.Popen([apport_path, str(test_proc), '42', '0'],
466
 
                                   close_fds=True, stdin=subprocess.PIPE, env=env,
 
468
                                   stdin=subprocess.PIPE, env=env,
467
469
                                   stdout=subprocess.PIPE, stderr=subprocess.PIPE,
468
470
                                   universal_newlines=True)
469
471
            (out, err) = app.communicate(b'hel\x01lo')
500
502
            env = os.environ.copy()
501
503
            env['APPORT_LOG_FILE'] = '/not/existing/apport.log'
502
504
            app = subprocess.Popen([apport_path, str(test_proc), '42', '0'],
503
 
                                   close_fds=True, stdin=subprocess.PIPE, env=env,
 
505
                                   stdin=subprocess.PIPE, env=env,
504
506
                                   stdout=subprocess.PIPE, stderr=subprocess.PIPE,
505
507
                                   universal_newlines=True)
506
508
            (out, err) = app.communicate(b'hel\x01lo')
594
596
        self.assertGreater(timeout, 0)
595
597
        if check_running:
596
598
            self.assertEqual(subprocess.call(['pidof', command]), 1,
597
 
                    'no running test executable processes')
 
599
                             'no running test executable processes')
598
600
 
599
601
        if expect_corefile:
600
602
            self.assertTrue(os.path.exists('/tmp/core'), 'leaves wanted core file')
601
603
            try:
602
604
                # check that core file is valid
603
605
                gdb = subprocess.Popen(['gdb', '--batch', '--ex', 'bt',
604
 
                    command, '/tmp/core'], stdout=subprocess.PIPE,
605
 
                    stderr=subprocess.PIPE)
 
606
                                        command, '/tmp/core'],
 
607
                                       stdout=subprocess.PIPE,
 
608
                                       stderr=subprocess.PIPE)
606
609
                (out, err) = gdb.communicate()
607
610
                self.assertEqual(gdb.returncode, 0)
608
611
                out = out.decode()
634
637
        self.assertGreater(len(r['CoreDump']), 5000)
635
638
        r.add_gdb_info()
636
639
        self.assertTrue('\n#2' in r.get('Stacktrace', ''),
637
 
                r.get('Stacktrace', 'no Stacktrace field'))
 
640
                        r.get('Stacktrace', 'no Stacktrace field'))
638
641
 
639
642
#
640
643
# main
649
652
 
650
653
if apport.fileutils.get_all_reports():
651
654
    sys.stderr.write('Please remove all crash reports from /var/crash/ for this test suite:\n  %s\n' %
652
 
            '\n  '.join(os.listdir('/var/crash')))
 
655
                     '\n  '.join(os.listdir('/var/crash')))
653
656
    sys.exit(1)
654
657
 
655
658
unittest.main()