Back to index

apport  2.4
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 638 of file test_signal_crashes.py.

00638 
00639     def check_report_coredump(self, report_path):
00640         '''Check that given report file has a valid core dump'''
00641 
00642         r = apport.Report()
00643         with open(report_path, 'rb') as f:
00644             r.load(f)
00645         self.assertTrue('CoreDump' in r)
00646         self.assertGreater(len(r['CoreDump']), 5000)
00647         r.add_gdb_info()
00648         self.assertTrue('\n#2' in r.get('Stacktrace', ''),
00649                         r.get('Stacktrace', 'no Stacktrace field'))
00650 
00651 #
00652 # main
00653 #

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 542 of file test_signal_crashes.py.

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

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

00629 
00630     def get_temp_all_reports(self):
00631         '''Call apport.fileutils.get_all_reports() for our temp dir'''
00632 
00633         old_dir = apport.fileutils.report_dir
00634         apport.fileutils.report_dir = self.report_dir
00635         reports = apport.fileutils.get_all_reports()
00636         apport.fileutils.report_dir = old_dir
00637         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 279 of file test_signal_crashes.py.

00279 
00280     def test_core_dump_packaged(self):
00281         '''packaged executables create core dumps on proper ulimits'''
00282 
00283         # for SIGSEGV
00284         resource.setrlimit(resource.RLIMIT_CORE, (1, -1))
00285         self.do_crash(expect_coredump=False, expect_corefile=False)
00286         self.assertEqual(apport.fileutils.get_all_reports(), [])
00287         resource.setrlimit(resource.RLIMIT_CORE, (10, -1))
00288         self.do_crash(expect_corefile=False)
00289         self.assertEqual(apport.fileutils.get_all_reports(), [self.test_report])
00290         self.check_report_coredump(self.test_report)
00291         apport.fileutils.delete_report(self.test_report)
00292         resource.setrlimit(resource.RLIMIT_CORE, (10000, -1))
00293         self.do_crash(expect_corefile=True)
00294         self.assertEqual(apport.fileutils.get_all_reports(), [self.test_report])
00295         self.check_report_coredump(self.test_report)
00296         apport.fileutils.delete_report(self.test_report)
00297         resource.setrlimit(resource.RLIMIT_CORE, (-1, -1))
00298         self.do_crash(expect_corefile=True)
00299         self.assertEqual(apport.fileutils.get_all_reports(), [self.test_report])
00300         self.check_report_coredump(self.test_report)
00301         apport.fileutils.delete_report(self.test_report)
00302 
00303         # for SIGABRT
00304         resource.setrlimit(resource.RLIMIT_CORE, (1, -1))
00305         self.do_crash(expect_coredump=False, expect_corefile=False, sig=signal.SIGABRT)
00306         self.assertEqual(apport.fileutils.get_all_reports(), [])
00307         resource.setrlimit(resource.RLIMIT_CORE, (10, -1))
00308         self.do_crash(expect_corefile=False, 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, (10000, -1))
00312         self.do_crash(expect_corefile=True, sig=signal.SIGABRT)
00313         self.assertEqual(apport.fileutils.get_all_reports(), [self.test_report])
00314         apport.fileutils.delete_report(self.test_report)
00315         resource.setrlimit(resource.RLIMIT_CORE, (-1, -1))
00316         self.do_crash(expect_corefile=True, sig=signal.SIGABRT)
00317         self.assertEqual(apport.fileutils.get_all_reports(), [self.test_report])
00318 
00319         # creates core file with existing crash report, too
00320         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 321 of file test_signal_crashes.py.

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

Here is the call graph for this function:

report generation with apport

Definition at line 80 of file test_signal_crashes.py.

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

Here is the call graph for this function:

limitation of crash report flood

Definition at line 252 of file test_signal_crashes.py.

00252 
00253     def test_flood_limit(self):
00254         '''limitation of crash report flood'''
00255 
00256         count = 0
00257         while count < 7:
00258             sys.stderr.write('%i ' % count)
00259             sys.stderr.flush()
00260             self.do_crash()
00261             reports = apport.fileutils.get_new_reports()
00262             if not reports:
00263                 break
00264             apport.fileutils.mark_report_seen(self.test_report)
00265             count += 1
00266         self.assertGreater(count, 1, 'gets at least 2 repeated crashes')
00267         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 410 of file test_signal_crashes.py.

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

Here is the call graph for this function:

apport ignores SIGQUIT

Definition at line 226 of file test_signal_crashes.py.

00226 
00227     def test_ignore_sigquit(self):
00228         '''apport ignores SIGQUIT'''
00229 
00230         self.do_crash(sig=signal.SIGQUIT)
00231         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 232 of file test_signal_crashes.py.

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

Here is the call graph for this function:

core dumps are capped on available memory size

Definition at line 357 of file test_signal_crashes.py.

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

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

Here is the call graph for this function:

outputs to log file, if available

Definition at line 465 of file test_signal_crashes.py.

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

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

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

Here is the call graph for this function:

core dump works for non-writable cwd

Definition at line 268 of file test_signal_crashes.py.

00268 
00269     def test_nonwritable_cwd(self):
00270         '''core dump works for non-writable cwd'''
00271 
00272         os.chdir('/')
00273         self.do_crash()
00274         pr = apport.Report()
00275         self.assertTrue(os.path.exists(self.test_report))
00276         with open(self.test_report, 'rb') as f:
00277             pr.load(f)
00278         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 129 of file test_signal_crashes.py.

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

Here is the call graph for this function:

unpackaged binaries do not create a report

Definition at line 198 of file test_signal_crashes.py.

00198 
00199     def test_unpackaged_binary(self):
00200         '''unpackaged binaries do not create a report'''
00201 
00202         local_exe = os.path.join(self.workdir, 'mybin')
00203         with open(local_exe, 'wb') as dest:
00204             with open(test_executable, 'rb') as src:
00205                 dest.write(src.read())
00206         os.chmod(local_exe, 0o755)
00207         self.do_crash(command=local_exe)
00208         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 209 of file test_signal_crashes.py.

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