Back to index

apport  2.3
Public Member Functions | Public Attributes
test_signal_crashes.T Class Reference

List of all members.

Public Member Functions

def setUp
def tearDown
def test_empty_core_dump
def test_crash_apport
def test_parallel_crash
def test_lock_symlink
def test_unpackaged_binary
def test_unpackaged_script
def test_ignore_sigquit
def test_leak_inaccessible_files
def test_flood_limit
def test_nonwritable_cwd
def test_core_dump_packaged
def test_core_dump_unpackaged
def test_limit_size
def test_ignore
def test_modify_after_start
def test_logging_file
def test_logging_stderr
def create_test_process
def do_crash
def get_temp_all_reports
def check_report_coredump

Public Attributes

 report_dir
 workdir
 test_report

Detailed Description

Definition at line 25 of file test_signal_crashes.py.


Member Function Documentation

def test_signal_crashes.T.check_report_coredump (   self,
  report_path 
)
Check that given report file has a valid core dump

Definition at line 630 of file test_signal_crashes.py.

00630 
00631     def check_report_coredump(self, report_path):
00632         '''Check that given report file has a valid core dump'''
00633 
00634         r = apport.Report()
00635         with open(report_path, 'rb') as f:
00636             r.load(f)
00637         self.assertTrue('CoreDump' in r)
00638         self.assertGreater(len(r['CoreDump']), 5000)
00639         r.add_gdb_info()
00640         self.assertTrue('\n#2' in r.get('Stacktrace', ''),
00641                         r.get('Stacktrace', 'no Stacktrace field'))
00642 
00643 #
00644 # main
00645 #

Here is the caller graph for this function:

def test_signal_crashes.T.create_test_process (   klass,
  check_running = True,
  command = test_executable 
)
Spawn test_executable.

Wait until it is fully running, and return its PID.

Definition at line 536 of file test_signal_crashes.py.

00536 
00537     def create_test_process(klass, check_running=True, command=test_executable):
00538         '''Spawn test_executable.
00539 
00540         Wait until it is fully running, and return its PID.
00541         '''
00542         assert os.access(command, os.X_OK), command + ' is not executable'
00543         if check_running:
00544             assert subprocess.call(['pidof', command]) == 1, 'no running test executable processes'
00545         pid = os.fork()
00546         if pid == 0:
00547             os.dup2(os.open('/dev/null', os.O_WRONLY), sys.stdout.fileno())
00548             sys.stdin.close()
00549             os.setsid()
00550             os.execv(command, [command])
00551             assert False, 'Could not execute ' + command
00552 
00553         # wait until child process has execv()ed properly
00554         while True:
00555             with open('/proc/%i/cmdline' % pid) as f:
00556                 cmdline = f.read()
00557             if 'test_signal' in cmdline:
00558                 time.sleep(0.1)
00559             else:
00560                 break
00561 
00562         time.sleep(0.3)  # needs some more setup time
00563         return pid

Here is the call graph for this function:

Here is the caller graph for this function:

def test_signal_crashes.T.do_crash (   self,
  expect_coredump = True,
  expect_corefile = False,
  sig = signal.SIGSEGV,
  check_running = True,
  sleep = 0,
  command = test_executable 
)
Generate a test crash.

This runs command (by default test_executable) in /tmp, lets it crash,
and checks that it exits with the expected return code, leaving a core
file behind if expect_corefile is set, and generating a crash report if
expect_coredump is set.

If check_running is set (default), this will abort if test_process is
already running.

Definition at line 566 of file test_signal_crashes.py.

00566 
00567                  command=test_executable):
00568         '''Generate a test crash.
00569 
00570         This runs command (by default test_executable) in /tmp, lets it crash,
00571         and checks that it exits with the expected return code, leaving a core
00572         file behind if expect_corefile is set, and generating a crash report if
00573         expect_coredump is set.
00574 
00575         If check_running is set (default), this will abort if test_process is
00576         already running.
00577         '''
00578         self.assertFalse(os.path.exists('core'), '/tmp/core already exists, please clean up first')
00579         pid = self.create_test_process(check_running, command)
00580         if sleep > 0:
00581             time.sleep(sleep)
00582         os.kill(pid, sig)
00583         result = os.waitpid(pid, 0)[1]
00584         self.assertFalse(os.WIFEXITED(result), 'test process did not exit normally')
00585         self.assertTrue(os.WIFSIGNALED(result), 'test process died due to signal')
00586         self.assertEqual(os.WCOREDUMP(result), expect_coredump)
00587         self.assertEqual(os.WSTOPSIG(result), 0, 'test process was not signaled to stop')
00588         self.assertEqual(os.WTERMSIG(result), sig, 'test process died due to proper signal')
00589 
00590         # wait max 10 seconds for apport to finish
00591         timeout = 50
00592         while timeout >= 0:
00593             if subprocess.call(['pidof', '-x', 'apport'], stdout=subprocess.PIPE) != 0:
00594                 break
00595             time.sleep(0.2)
00596             timeout -= 1
00597         self.assertGreater(timeout, 0)
00598         if check_running:
00599             self.assertEqual(subprocess.call(['pidof', command]), 1,
00600                              'no running test executable processes')
00601 
00602         if expect_corefile:
00603             self.assertTrue(os.path.exists('/tmp/core'), 'leaves wanted core file')
00604             try:
00605                 # check that core file is valid
00606                 gdb = subprocess.Popen(['gdb', '--batch', '--ex', 'bt',
00607                                         command, '/tmp/core'],
00608                                        stdout=subprocess.PIPE,
00609                                        stderr=subprocess.PIPE)
00610                 (out, err) = gdb.communicate()
00611                 self.assertEqual(gdb.returncode, 0)
00612                 out = out.decode()
00613                 err = err.decode().strip()
00614                 self.assertTrue(err == '' or err.startswith('warning'), err)
00615             finally:
00616                 os.unlink('/tmp/core')
00617         else:
00618             if os.path.exists('/tmp/core'):
00619                 os.unlink('/tmp/core')
00620                 self.fail('leaves unexpected core file behind')

Here is the call graph for this function:

Here is the caller graph for this function:

Call apport.fileutils.get_all_reports() for our temp dir

Definition at line 621 of file test_signal_crashes.py.

00621 
00622     def get_temp_all_reports(self):
00623         '''Call apport.fileutils.get_all_reports() for our temp dir'''
00624 
00625         old_dir = apport.fileutils.report_dir
00626         apport.fileutils.report_dir = self.report_dir
00627         reports = apport.fileutils.get_all_reports()
00628         apport.fileutils.report_dir = old_dir
00629         return reports

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 26 of file test_signal_crashes.py.

00026 
00027     def setUp(self):
00028         # use local report dir
00029         self.report_dir = tempfile.mkdtemp()
00030         os.environ['APPORT_REPORT_DIR'] = self.report_dir
00031 
00032         self.workdir = tempfile.mkdtemp()
00033 
00034         # move aside current ignore file
00035         if os.path.exists(ifpath):
00036             os.rename(ifpath, ifpath + '.apporttest')
00037 
00038         # do not write core files by default
00039         resource.setrlimit(resource.RLIMIT_CORE, (0, -1))
00040 
00041         # that's the place where to put core dumps, etc.
00042         os.chdir('/tmp')
00043 
00044         # expected report name for test executable report
00045         self.test_report = os.path.join(
00046             apport.fileutils.report_dir, '%s.%i.crash' %
00047             (test_executable.replace('/', '_'), os.getuid()))

Definition at line 48 of file test_signal_crashes.py.

00048 
00049     def tearDown(self):
00050         shutil.rmtree(self.report_dir)
00051         shutil.rmtree(self.workdir)
00052 
00053         # clean up our ignore file
00054         if os.path.exists(ifpath):
00055             os.unlink(ifpath)
00056         orig_ignore_file = ifpath + '.apporttest'
00057         if os.path.exists(orig_ignore_file):
00058             os.rename(orig_ignore_file, ifpath)
00059 
00060         # permit tests to leave behind test_report, but nothing else
00061         if os.path.exists(self.test_report):
00062             apport.fileutils.delete_report(self.test_report)
00063         self.assertEqual(apport.fileutils.get_all_reports(), [])

Here is the call graph for this function:

packaged executables create core dumps on proper ulimits

Definition at line 275 of file test_signal_crashes.py.

00275 
00276     def test_core_dump_packaged(self):
00277         '''packaged executables create core dumps on proper ulimits'''
00278 
00279         # for SIGSEGV
00280         resource.setrlimit(resource.RLIMIT_CORE, (1, -1))
00281         self.do_crash(expect_coredump=False, expect_corefile=False)
00282         self.assertEqual(apport.fileutils.get_all_reports(), [])
00283         resource.setrlimit(resource.RLIMIT_CORE, (10, -1))
00284         self.do_crash(expect_corefile=False)
00285         self.assertEqual(apport.fileutils.get_all_reports(), [self.test_report])
00286         self.check_report_coredump(self.test_report)
00287         apport.fileutils.delete_report(self.test_report)
00288         resource.setrlimit(resource.RLIMIT_CORE, (10000, -1))
00289         self.do_crash(expect_corefile=True)
00290         self.assertEqual(apport.fileutils.get_all_reports(), [self.test_report])
00291         self.check_report_coredump(self.test_report)
00292         apport.fileutils.delete_report(self.test_report)
00293         resource.setrlimit(resource.RLIMIT_CORE, (-1, -1))
00294         self.do_crash(expect_corefile=True)
00295         self.assertEqual(apport.fileutils.get_all_reports(), [self.test_report])
00296         self.check_report_coredump(self.test_report)
00297         apport.fileutils.delete_report(self.test_report)
00298 
00299         # for SIGABRT
00300         resource.setrlimit(resource.RLIMIT_CORE, (1, -1))
00301         self.do_crash(expect_coredump=False, expect_corefile=False, sig=signal.SIGABRT)
00302         self.assertEqual(apport.fileutils.get_all_reports(), [])
00303         resource.setrlimit(resource.RLIMIT_CORE, (10, -1))
00304         self.do_crash(expect_corefile=False, sig=signal.SIGABRT)
00305         self.assertEqual(apport.fileutils.get_all_reports(), [self.test_report])
00306         apport.fileutils.delete_report(self.test_report)
00307         resource.setrlimit(resource.RLIMIT_CORE, (10000, -1))
00308         self.do_crash(expect_corefile=True, sig=signal.SIGABRT)
00309         self.assertEqual(apport.fileutils.get_all_reports(), [self.test_report])
00310         apport.fileutils.delete_report(self.test_report)
00311         resource.setrlimit(resource.RLIMIT_CORE, (-1, -1))
00312         self.do_crash(expect_corefile=True, sig=signal.SIGABRT)
00313         self.assertEqual(apport.fileutils.get_all_reports(), [self.test_report])
00314 
00315         # creates core file with existing crash report, too
00316         self.do_crash(expect_corefile=True)

Here is the call graph for this function:

unpackaged executables create core dumps on proper ulimits

Definition at line 317 of file test_signal_crashes.py.

00317 
00318     def test_core_dump_unpackaged(self):
00319         '''unpackaged executables create core dumps on proper ulimits'''
00320 
00321         local_exe = os.path.join(self.workdir, 'mybin')
00322         with open(local_exe, 'wb') as dest:
00323             with open(test_executable, 'rb') as src:
00324                 dest.write(src.read())
00325         os.chmod(local_exe, 0o755)
00326 
00327         # for SIGSEGV
00328         resource.setrlimit(resource.RLIMIT_CORE, (1, -1))
00329         self.do_crash(expect_coredump=False, expect_corefile=False, command=local_exe)
00330         self.assertEqual(apport.fileutils.get_all_reports(), [])
00331         resource.setrlimit(resource.RLIMIT_CORE, (10, -1))
00332         self.assertEqual(apport.fileutils.get_all_reports(), [])
00333         resource.setrlimit(resource.RLIMIT_CORE, (10000, -1))
00334         self.do_crash(expect_corefile=True, command=local_exe)
00335         self.assertEqual(apport.fileutils.get_all_reports(), [])
00336         resource.setrlimit(resource.RLIMIT_CORE, (-1, -1))
00337         self.do_crash(expect_corefile=True, command=local_exe)
00338         self.assertEqual(apport.fileutils.get_all_reports(), [])
00339 
00340         # for SIGABRT
00341         resource.setrlimit(resource.RLIMIT_CORE, (1, -1))
00342         self.do_crash(expect_coredump=False, expect_corefile=False, command=local_exe, sig=signal.SIGABRT)
00343         self.assertEqual(apport.fileutils.get_all_reports(), [])
00344         resource.setrlimit(resource.RLIMIT_CORE, (10, -1))
00345         self.do_crash(expect_corefile=False, command=local_exe, sig=signal.SIGABRT)
00346         self.assertEqual(apport.fileutils.get_all_reports(), [])
00347         resource.setrlimit(resource.RLIMIT_CORE, (10000, -1))
00348         self.do_crash(expect_corefile=True, command=local_exe, sig=signal.SIGABRT)
00349         self.assertEqual(apport.fileutils.get_all_reports(), [])
00350         resource.setrlimit(resource.RLIMIT_CORE, (-1, -1))
00351         self.do_crash(expect_corefile=True, command=local_exe, sig=signal.SIGABRT)
00352         self.assertEqual(apport.fileutils.get_all_reports(), [])

Here is the call graph for this function:

report generation with apport

Definition at line 79 of file test_signal_crashes.py.

00079 
00080     def test_crash_apport(self):
00081         '''report generation with apport'''
00082 
00083         self.do_crash()
00084 
00085         # check crash report
00086         self.assertEqual(apport.fileutils.get_all_reports(), [self.test_report])
00087         st = os.stat(self.test_report)
00088         self.assertEqual(stat.S_IMODE(st.st_mode), 0o640, 'report has correct permissions')
00089 
00090         # a subsequent crash does not alter unseen report
00091         self.do_crash()
00092         st2 = os.stat(self.test_report)
00093         self.assertEqual(st, st2, 'original unseen report did not get overwritten')
00094 
00095         # a subsequent crash alters seen report
00096         apport.fileutils.mark_report_seen(self.test_report)
00097         self.do_crash()
00098         st2 = os.stat(self.test_report)
00099         self.assertNotEqual(st, st2, 'original seen report gets overwritten')
00100 
00101         pr = apport.Report()
00102         with open(self.test_report, 'rb') as f:
00103             pr.load(f)
00104         self.assertTrue(set(required_fields).issubset(set(pr.keys())),
00105                         'report has required fields')
00106         self.assertEqual(pr['ExecutablePath'], test_executable)
00107         self.assertEqual(pr['ProcCmdline'], test_executable)
00108         self.assertEqual(pr['Signal'], '%i' % signal.SIGSEGV)
00109 
00110         # check safe environment subset
00111         allowed_vars = ['SHELL', 'PATH', 'LANGUAGE', 'LANG', 'LC_CTYPE',
00112                         'LC_COLLATE', 'LC_TIME', 'LC_NUMERIC', 'LC_MONETARY',
00113                         'LC_MESSAGES', 'LC_PAPER', 'LC_NAME', 'LC_ADDRESS',
00114                         'LC_TELEPHONE', 'LC_MEASUREMENT', 'LC_IDENTIFICATION',
00115                         'LOCPATH', 'TERM']
00116 
00117         for l in pr['ProcEnviron'].splitlines():
00118             (k, v) = l.split('=', 1)
00119             self.assertTrue(k in allowed_vars,
00120                             'report contains sensitive environment variable %s' % k)
00121 
00122         # UserGroups only has system groups
00123         for g in pr['UserGroups'].split():
00124             self.assertLess(grp.getgrnam(g).gr_gid, 500)
00125 
00126         self.assertFalse('root' in pr['UserGroups'],
00127                          'collected system groups are not those from root')

Here is the call graph for this function:

empty core dumps do not generate a report

Definition at line 64 of file test_signal_crashes.py.

00064 
00065     def test_empty_core_dump(self):
00066         '''empty core dumps do not generate a report'''
00067 
00068         test_proc = self.create_test_process()
00069         try:
00070             app = subprocess.Popen([apport_path, str(test_proc), '42', '0'],
00071                                    stdin=subprocess.PIPE, stderr=subprocess.PIPE)
00072             app.stdin.close()
00073             assert app.wait() == 0, app.stderr.read()
00074         finally:
00075             os.kill(test_proc, 9)
00076             os.waitpid(test_proc, 0)
00077 
00078         self.assertEqual(self.get_temp_all_reports(), [])

Here is the call graph for this function:

limitation of crash report flood

Definition at line 248 of file test_signal_crashes.py.

00248 
00249     def test_flood_limit(self):
00250         '''limitation of crash report flood'''
00251 
00252         count = 0
00253         while count < 7:
00254             sys.stderr.write('%i ' % count)
00255             sys.stderr.flush()
00256             self.do_crash()
00257             reports = apport.fileutils.get_new_reports()
00258             if not reports:
00259                 break
00260             apport.fileutils.mark_report_seen(self.test_report)
00261             count += 1
00262         self.assertGreater(count, 1, 'gets at least 2 repeated crashes')
00263         self.assertLess(count, 7, 'stops flooding after less than 7 repeated crashes')

Here is the call graph for this function:

ignoring executables

Definition at line 405 of file test_signal_crashes.py.

00405 
00406     def test_ignore(self):
00407         '''ignoring executables'''
00408 
00409         self.do_crash()
00410 
00411         reports = apport.fileutils.get_all_reports()
00412         self.assertEqual(len(reports), 1)
00413 
00414         pr = apport.Report()
00415         with open(reports[0], 'rb') as f:
00416             pr.load(f)
00417         os.unlink(reports[0])
00418 
00419         pr.mark_ignore()
00420 
00421         self.do_crash()
00422         self.assertEqual(apport.fileutils.get_all_reports(), [])

Here is the call graph for this function:

apport ignores SIGQUIT

Definition at line 222 of file test_signal_crashes.py.

00222 
00223     def test_ignore_sigquit(self):
00224         '''apport ignores SIGQUIT'''
00225 
00226         self.do_crash(sig=signal.SIGQUIT)
00227         self.assertEqual(apport.fileutils.get_all_reports(), [])

Here is the call graph for this function:

existence of user-inaccessible files does not leak

Definition at line 228 of file test_signal_crashes.py.

00228 
00229     def test_leak_inaccessible_files(self):
00230         '''existence of user-inaccessible files does not leak'''
00231 
00232         local_exe = os.path.join(self.workdir, 'myscript')
00233         with open(local_exe, 'w') as f:
00234             f.write('#!/usr/bin/perl\nsystem("mv $0 $0.exe");\nsystem("ln -sf /etc/shadow $0");\n$0="..$0";\nsleep(10);\n')
00235         os.chmod(local_exe, 0o755)
00236         self.do_crash(check_running=False, command=local_exe, sleep=2)
00237 
00238         leak = os.path.join(apport.fileutils.report_dir, '_usr_bin_perl.%i.crash' %
00239                             (os.getuid()))
00240         pr = apport.Report()
00241         with open(leak, 'rb') as f:
00242             pr.load(f)
00243         # On a leak, no report is created since the executable path will be replaced
00244         # by the symlink path, and it doesn't belong to any package.
00245         self.assertEqual(pr['ExecutablePath'], '/usr/bin/perl')
00246         self.assertFalse('InterpreterPath' in pr)
00247         apport.fileutils.delete_report(leak)

Here is the call graph for this function:

core dumps are capped on available memory size

Definition at line 353 of file test_signal_crashes.py.

00353 
00354     def test_limit_size(self):
00355         '''core dumps are capped on available memory size'''
00356 
00357         # determine how much data we have to pump into apport in order to make sure
00358         # that it will refuse the core dump
00359         r = apport.Report()
00360         with open('/proc/meminfo', 'rb') as f:
00361             r.load(f)
00362         totalmb = int(r['MemFree'].split()[0]) + int(r['Cached'].split()[0])
00363         totalmb = int(totalmb / 1024)
00364         r = None
00365 
00366         test_proc = self.create_test_process()
00367         try:
00368             app = subprocess.Popen([apport_path, str(test_proc), '42', '0'],
00369                                    stdin=subprocess.PIPE, stderr=subprocess.PIPE)
00370             # pipe an entire total memory size worth of spaces into it, which must be
00371             # bigger than the 'usable' memory size. apport should digest that and the
00372             # report should not have a core dump; NB that this should error out
00373             # with a SIGPIPE when apport aborts reading from stdin
00374             onemb = b' ' * 1048576
00375             while totalmb > 0:
00376                 if totalmb & 31 == 0:
00377                     sys.stderr.write('.')
00378                     sys.stderr.flush()
00379                 try:
00380                     app.stdin.write(onemb)
00381                 except IOError as e:
00382                     if e.errno == errno.EPIPE:
00383                         break
00384                     else:
00385                         raise
00386                 totalmb -= 1
00387             app.stdin.close()
00388             self.assertEqual(app.wait(), 0, app.stderr.read())
00389             onemb = None
00390         finally:
00391             os.kill(test_proc, 9)
00392             os.waitpid(test_proc, 0)
00393 
00394         reports = self.get_temp_all_reports()
00395         self.assertEqual(len(reports), 1)
00396 
00397         pr = apport.Report()
00398         with open(reports[0], 'rb') as f:
00399             pr.load(f)
00400         os.unlink(reports[0])
00401 
00402         self.assertEqual(pr['Signal'], '42')
00403         self.assertEqual(pr['ExecutablePath'], test_executable)
00404         self.assertFalse('CoreDump' in pr)

Here is the call graph for this function:

existing .lock file as dangling symlink does not create the file

This would be a vulnerability, as users could overwrite system files.

Definition at line 167 of file test_signal_crashes.py.

00167 
00168     def test_lock_symlink(self):
00169         '''existing .lock file as dangling symlink does not create the file
00170 
00171         This would be a vulnerability, as users could overwrite system files.
00172         '''
00173         # prepare a symlink trap
00174         lockpath = os.path.join(self.report_dir, '.lock')
00175         trappath = os.path.join(self.report_dir, '0wned')
00176         os.symlink(trappath, lockpath)
00177 
00178         # now call apport
00179         test_proc = self.create_test_process()
00180 
00181         try:
00182             app = subprocess.Popen([apport_path, str(test_proc), '42', '0'],
00183                                    stdin=subprocess.PIPE, stderr=subprocess.PIPE)
00184             app.stdin.write(b'boo')
00185             app.stdin.close()
00186 
00187             self.assertNotEqual(app.wait(), 0, app.stderr.read())
00188         finally:
00189             os.kill(test_proc, 9)
00190             os.waitpid(test_proc, 0)
00191 
00192         self.assertEqual(self.get_temp_all_reports(), [])
00193         self.assertFalse(os.path.exists(trappath))

Here is the call graph for this function:

outputs to log file, if available

Definition at line 459 of file test_signal_crashes.py.

00459 
00460     def test_logging_file(self):
00461         '''outputs to log file, if available'''
00462 
00463         test_proc = self.create_test_process()
00464         log = os.path.join(self.workdir, 'apport.log')
00465         try:
00466             env = os.environ.copy()
00467             env['APPORT_LOG_FILE'] = log
00468             app = subprocess.Popen([apport_path, str(test_proc), '42', '0'],
00469                                    stdin=subprocess.PIPE, env=env,
00470                                    stdout=subprocess.PIPE, stderr=subprocess.PIPE,
00471                                    universal_newlines=True)
00472             (out, err) = app.communicate(b'hel\x01lo')
00473         finally:
00474             os.kill(test_proc, 9)
00475             os.waitpid(test_proc, 0)
00476 
00477         self.assertEqual(out, '')
00478         self.assertEqual(err, '')
00479         self.assertEqual(app.returncode, 0, err)
00480         with open(log) as f:
00481             logged = f.read()
00482         self.assertTrue('called for pid' in logged, logged)
00483         self.assertTrue('wrote report' in logged, logged)
00484         self.assertFalse('Traceback' in logged, logged)
00485 
00486         reports = self.get_temp_all_reports()
00487         self.assertEqual(len(reports), 1)
00488 
00489         pr = apport.Report()
00490         with open(reports[0], 'rb') as f:
00491             pr.load(f)
00492         os.unlink(reports[0])
00493 
00494         self.assertEqual(pr['Signal'], '42')
00495         self.assertEqual(pr['ExecutablePath'], test_executable)
00496         self.assertEqual(pr['CoreDump'], b'hel\x01lo')

Here is the call graph for this function:

outputs to stderr if log is not available

Definition at line 497 of file test_signal_crashes.py.

00497 
00498     def test_logging_stderr(self):
00499         '''outputs to stderr if log is not available'''
00500 
00501         test_proc = self.create_test_process()
00502         try:
00503             env = os.environ.copy()
00504             env['APPORT_LOG_FILE'] = '/not/existing/apport.log'
00505             app = subprocess.Popen([apport_path, str(test_proc), '42', '0'],
00506                                    stdin=subprocess.PIPE, env=env,
00507                                    stdout=subprocess.PIPE, stderr=subprocess.PIPE,
00508                                    universal_newlines=True)
00509             (out, err) = app.communicate(b'hel\x01lo')
00510         finally:
00511             os.kill(test_proc, 9)
00512             os.waitpid(test_proc, 0)
00513 
00514         self.assertEqual(out, '')
00515         self.assertEqual(app.returncode, 0, err)
00516         self.assertTrue('called for pid' in err, err)
00517         self.assertTrue('wrote report' in err, err)
00518         self.assertFalse('Traceback' in err, err)
00519 
00520         reports = self.get_temp_all_reports()
00521         self.assertEqual(len(reports), 1)
00522 
00523         pr = apport.Report()
00524         with open(reports[0], 'rb') as f:
00525             pr.load(f)
00526         os.unlink(reports[0])
00527 
00528         self.assertEqual(pr['Signal'], '42')
00529         self.assertEqual(pr['ExecutablePath'], test_executable)
00530         self.assertEqual(pr['CoreDump'], b'hel\x01lo')

Here is the call graph for this function:

ignores executables which got modified after process started

Definition at line 423 of file test_signal_crashes.py.

00423 
00424     def test_modify_after_start(self):
00425         '''ignores executables which got modified after process started'''
00426 
00427         # create executable in a path we can modify which apport regards as
00428         # likely packaged
00429         (fd, myexe) = tempfile.mkstemp(dir='/var/tmp')
00430         try:
00431             with open(test_executable, 'rb') as f:
00432                 os.write(fd, f.read())
00433             os.close(fd)
00434             os.chmod(myexe, 0o755)
00435             time.sleep(1)
00436 
00437             try:
00438                 test_proc = self.create_test_process(command=myexe)
00439 
00440                 # bump mtime of myexe to make it more recent than process start
00441                 # time; ensure this works with file systems with only second
00442                 # resolution
00443                 time.sleep(1.1)
00444                 os.utime(myexe, None)
00445 
00446                 app = subprocess.Popen([apport_path, str(test_proc), '42', '0'],
00447                                        stdin=subprocess.PIPE, stderr=subprocess.PIPE)
00448                 app.stdin.write(b'boo')
00449                 app.stdin.close()
00450                 err = app.stderr.read().decode()
00451                 self.assertNotEqual(app.wait(), 0, err)
00452             finally:
00453                 os.kill(test_proc, 9)
00454                 os.waitpid(test_proc, 0)
00455 
00456             self.assertEqual(self.get_temp_all_reports(), [])
00457         finally:
00458             os.unlink(myexe)

Here is the call graph for this function:

core dump works for non-writable cwd

Definition at line 264 of file test_signal_crashes.py.

00264 
00265     def test_nonwritable_cwd(self):
00266         '''core dump works for non-writable cwd'''
00267 
00268         os.chdir('/')
00269         self.do_crash()
00270         pr = apport.Report()
00271         self.assertTrue(os.path.exists(self.test_report))
00272         with open(self.test_report, 'rb') as f:
00273             pr.load(f)
00274         assert set(required_fields).issubset(set(pr.keys()))

Here is the call graph for this function:

only one apport instance is ran at a time

Definition at line 128 of file test_signal_crashes.py.

00128 
00129     def test_parallel_crash(self):
00130         '''only one apport instance is ran at a time'''
00131 
00132         test_proc = self.create_test_process()
00133         test_proc2 = self.create_test_process(False, '/bin/dd')
00134         try:
00135             app = subprocess.Popen([apport_path, str(test_proc), '42', '0'],
00136                                    stdin=subprocess.PIPE, stderr=subprocess.PIPE)
00137 
00138             time.sleep(0.5)  # give it some time to grab the lock
00139 
00140             app2 = subprocess.Popen([apport_path, str(test_proc2), '42', '0'],
00141                                     stdin=subprocess.PIPE, stderr=subprocess.PIPE)
00142 
00143             # app should wait indefinitely for stdin, while app2 should terminate
00144             # immediately (give it 5 seconds)
00145             timeout = 50
00146             while timeout >= 0:
00147                 if app2.poll():
00148                     break
00149 
00150                 time.sleep(0.1)
00151                 timeout -= 1
00152 
00153             self.assertGreater(timeout, 0, 'second apport instance terminates immediately')
00154             self.assertFalse(app.poll(), 'first apport instance is still running')
00155 
00156             # properly terminate app and app2
00157             app2.stdin.close()
00158             app.stdin.write(b'boo')
00159             app.stdin.close()
00160 
00161             self.assertEqual(app.wait(), 0, app.stderr.read())
00162         finally:
00163             os.kill(test_proc, 9)
00164             os.waitpid(test_proc, 0)
00165             os.kill(test_proc2, 9)
00166             os.waitpid(test_proc2, 0)

Here is the call graph for this function:

unpackaged binaries do not create a report

Definition at line 194 of file test_signal_crashes.py.

00194 
00195     def test_unpackaged_binary(self):
00196         '''unpackaged binaries do not create a report'''
00197 
00198         local_exe = os.path.join(self.workdir, 'mybin')
00199         with open(local_exe, 'wb') as dest:
00200             with open(test_executable, 'rb') as src:
00201                 dest.write(src.read())
00202         os.chmod(local_exe, 0o755)
00203         self.do_crash(command=local_exe)
00204         self.assertEqual(apport.fileutils.get_all_reports(), [])

Here is the call graph for this function:

unpackaged scripts do not create a report

Definition at line 205 of file test_signal_crashes.py.

00205 
00206     def test_unpackaged_script(self):
00207         '''unpackaged scripts do not create a report'''
00208 
00209         local_exe = os.path.join(self.workdir, 'myscript')
00210         with open(local_exe, 'w') as f:
00211             f.write('#!/bin/sh\nkill -SEGV $$')
00212         os.chmod(local_exe, 0o755)
00213         self.do_crash(command=local_exe)
00214 
00215         # absolute path
00216         self.assertEqual(apport.fileutils.get_all_reports(), [])
00217 
00218         # relative path
00219         os.chdir(self.workdir)
00220         self.do_crash(command='./myscript')
00221         self.assertEqual(apport.fileutils.get_all_reports(), [])

Here is the call graph for this function:


Member Data Documentation

Definition at line 28 of file test_signal_crashes.py.

Definition at line 44 of file test_signal_crashes.py.

Definition at line 31 of file test_signal_crashes.py.


The documentation for this class was generated from the following file: