Back to index

apport  2.3
Public Member Functions | Private Member Functions
test_python_crashes.T Class Reference

List of all members.

Public Member Functions

def tearDown
def test_general
def test_existing
def test_no_argv
def test_interactive
def test_ignoring
def test_no_flooding

Private Member Functions

def _test_crash
def _assert_no_reports

Detailed Description

Definition at line 22 of file test_python_crashes.py.


Member Function Documentation

def test_python_crashes.T._assert_no_reports (   self) [private]
Assert that there are no crash reports.

Definition at line 145 of file test_python_crashes.py.

00145 
00146     def _assert_no_reports(self):
00147         '''Assert that there are no crash reports.'''
00148 
00149         reports = apport.fileutils.get_new_reports()
00150         self.assertEqual(len(reports), 0,
00151                          'no crash reports present (cwd: %s)' % os.getcwd())

Here is the call graph for this function:

Here is the caller graph for this function:

def test_python_crashes.T._test_crash (   self,
  extracode = '',
  scriptname = None 
) [private]
Create a test crash.

Definition at line 27 of file test_python_crashes.py.

00027 
00028     def _test_crash(self, extracode='', scriptname=None):
00029         '''Create a test crash.'''
00030 
00031         # put the script into /var/tmp, since that isn't ignored in the
00032         # hook
00033         if scriptname:
00034             script = scriptname
00035             fd = os.open(scriptname, os.O_CREAT | os.O_WRONLY)
00036         else:
00037             (fd, script) = tempfile.mkstemp(dir='/var/tmp')
00038         try:
00039             os.write(fd, ('''#!/usr/bin/env %s
00040 import apport_python_hook
00041 apport_python_hook.install()
00042 
00043 def func(x):
00044     raise Exception(b'This should happen. \\xe2\\x99\\xa5'.decode('UTF-8'))
00045 
00046 %s
00047 func(42)
00048 ''' % (os.getenv('PYTHON', 'python3'), extracode)).encode())
00049             os.close(fd)
00050             os.chmod(script, 0o755)
00051 
00052             p = subprocess.Popen([script, 'testarg1', 'testarg2'],
00053                                  stdout=subprocess.PIPE,
00054                                  stderr=subprocess.PIPE, env=os.environ)
00055             err = p.communicate()[1].decode()
00056             self.assertEqual(p.returncode, 1,
00057                              'crashing test python program exits with failure code')
00058             self.assertTrue('This should happen.' in err, err)
00059             self.assertFalse('OSError' in err, err)
00060         finally:
00061             os.unlink(script)
00062 
00063         return script

Here is the caller graph for this function:

Definition at line 23 of file test_python_crashes.py.

00023 
00024     def tearDown(self):
00025         for f in apport.fileutils.get_all_reports():
00026             os.unlink(f)

Here is the call graph for this function:

Python crash hook overwrites seen existing files.

Definition at line 94 of file test_python_crashes.py.

00094 
00095     def test_existing(self):
00096         '''Python crash hook overwrites seen existing files.'''
00097 
00098         script = self._test_crash()
00099 
00100         # did we get a report?
00101         reports = apport.fileutils.get_new_reports()
00102         self.assertEqual(len(reports), 1, 'crashed Python program produced a report')
00103         self.assertEqual(stat.S_IMODE(os.stat(reports[0]).st_mode),
00104                          0o640, 'report has correct permissions')
00105 
00106         # touch report -> "seen" case
00107         apport.fileutils.mark_report_seen(reports[0])
00108 
00109         reports = apport.fileutils.get_new_reports()
00110         self.assertEqual(len(reports), 0)
00111 
00112         script = self._test_crash(scriptname=script)
00113         reports = apport.fileutils.get_new_reports()
00114         self.assertEqual(len(reports), 1)
00115 
00116         # "unseen" case
00117         script = self._test_crash(scriptname=script)
00118         reports = apport.fileutils.get_new_reports()
00119         self.assertEqual(len(reports), 1)

Here is the call graph for this function:

general operation of the Python crash hook.

Definition at line 64 of file test_python_crashes.py.

00064 
00065     def test_general(self):
00066         '''general operation of the Python crash hook.'''
00067 
00068         script = self._test_crash()
00069 
00070         # did we get a report?
00071         reports = apport.fileutils.get_new_reports()
00072         pr = None
00073         self.assertEqual(len(reports), 1, 'crashed Python program produced a report')
00074         self.assertEqual(stat.S_IMODE(os.stat(reports[0]).st_mode),
00075                          0o640, 'report has correct permissions')
00076 
00077         pr = problem_report.ProblemReport()
00078         with open(reports[0], 'rb') as f:
00079             pr.load(f)
00080 
00081         # check report contents
00082         expected_keys = ['InterpreterPath', 'PythonArgs', 'Traceback',
00083                          'ProblemType', 'ProcEnviron', 'ProcStatus',
00084                          'ProcCmdline', 'Date', 'ExecutablePath', 'ProcMaps',
00085                          'UserGroups']
00086         self.assertTrue(set(expected_keys).issubset(set(pr.keys())),
00087                         'report has necessary fields')
00088         self.assertTrue('bin/python' in pr['InterpreterPath'])
00089         self.assertEqual(pr['ExecutablePath'], script)
00090         self.assertEqual(pr['PythonArgs'], "['%s', 'testarg1', 'testarg2']" % script)
00091         self.assertTrue(pr['Traceback'].startswith('Traceback'))
00092         self.assertTrue("func\n    raise Exception(b'This should happen." in
00093                         pr['Traceback'], pr['Traceback'])

Here is the call graph for this function:

the Python crash hook respects the ignore list.

Definition at line 171 of file test_python_crashes.py.

00171 
00172     def test_ignoring(self):
00173         '''the Python crash hook respects the ignore list.'''
00174 
00175         # put the script into /var/crash, since that isn't ignored in the
00176         # hook
00177         (fd, script) = tempfile.mkstemp(dir=apport.fileutils.report_dir)
00178         ifpath = os.path.expanduser(apport.report._ignore_file)
00179         orig_ignore_file = None
00180         try:
00181             os.write(fd, ('''#!/usr/bin/env %s
00182 import apport_python_hook
00183 apport_python_hook.install()
00184 
00185 def func(x):
00186     raise Exception('This should happen.')
00187 
00188 func(42)
00189 ''' % os.getenv('PYTHON', 'python3')).encode('ascii'))
00190             os.close(fd)
00191             os.chmod(script, 0o755)
00192 
00193             # move aside current ignore file
00194             if os.path.exists(ifpath):
00195                 orig_ignore_file = ifpath + '.apporttest'
00196                 os.rename(ifpath, orig_ignore_file)
00197 
00198             # ignore
00199             r = apport.report.Report()
00200             r['ExecutablePath'] = script
00201             r.mark_ignore()
00202             r = None
00203 
00204             p = subprocess.Popen([script, 'testarg1', 'testarg2'],
00205                                  stdout=subprocess.PIPE, stderr=subprocess.PIPE)
00206             err = p.communicate()[1].decode()
00207             self.assertEqual(p.returncode, 1,
00208                              'crashing test python program exits with failure code')
00209             self.assertTrue('Exception: This should happen.' in err, err)
00210 
00211         finally:
00212             os.unlink(script)
00213             # clean up our ignore file
00214             if os.path.exists(ifpath):
00215                 os.unlink(ifpath)
00216             if orig_ignore_file:
00217                 os.rename(orig_ignore_file, ifpath)
00218 
00219         # did we get a report?
00220         reports = apport.fileutils.get_new_reports()
00221         self.assertEqual(len(reports), 0)

Here is the call graph for this function:

interactive Python sessions never generate a report.

Definition at line 152 of file test_python_crashes.py.

00152 
00153     def test_interactive(self):
00154         '''interactive Python sessions never generate a report.'''
00155 
00156         orig_cwd = os.getcwd()
00157         try:
00158             for d in ('/tmp', '/usr/local', '/usr'):
00159                 os.chdir(d)
00160                 p = subprocess.Popen(['python'], stdin=subprocess.PIPE,
00161                                      stdout=subprocess.PIPE, stderr=subprocess.PIPE)
00162                 (out, err) = p.communicate(b'raise ValueError')
00163                 out = out.decode()
00164                 err = err.decode()
00165                 assert p.returncode != 0
00166                 assert out == ''
00167                 assert 'ValueError' in err
00168                 self._assert_no_reports()
00169         finally:
00170             os.chdir(orig_cwd)

Here is the call graph for this function:

with zapped sys.argv.

Definition at line 120 of file test_python_crashes.py.

00120 
00121     def test_no_argv(self):
00122         '''with zapped sys.argv.'''
00123 
00124         self._test_crash('import sys\nsys.argv = None')
00125 
00126         # did we get a report?
00127         reports = apport.fileutils.get_new_reports()
00128         pr = None
00129         self.assertEqual(len(reports), 1, 'crashed Python program produced a report')
00130         self.assertEqual(stat.S_IMODE(os.stat(reports[0]).st_mode),
00131                          0o640, 'report has correct permissions')
00132 
00133         pr = problem_report.ProblemReport()
00134         with open(reports[0], 'rb') as f:
00135             pr.load(f)
00136 
00137         # check report contents
00138         expected_keys = ['InterpreterPath', 'Traceback', 'ProblemType',
00139                          'ProcEnviron', 'ProcStatus', 'ProcCmdline', 'Date',
00140                          'ExecutablePath', 'ProcMaps', 'UserGroups']
00141         self.assertTrue(set(expected_keys).issubset(set(pr.keys())),
00142                         'report has necessary fields')
00143         self.assertTrue('bin/python' in pr['InterpreterPath'])
00144         self.assertTrue(pr['Traceback'].startswith('Traceback'))

Here is the call graph for this function:

limit successive reports

Definition at line 222 of file test_python_crashes.py.

00222 
00223     def test_no_flooding(self):
00224         '''limit successive reports'''
00225 
00226         count = 0
00227         limit = 5
00228         while count < limit:
00229             self._test_crash(scriptname='/var/tmp/pytestcrash')
00230             reports = apport.fileutils.get_new_reports()
00231             if not reports:
00232                 break
00233             self.assertEqual(len(reports), 1, 'crashed Python program produced one report')
00234             apport.fileutils.mark_report_seen(reports[0])
00235             count += 1
00236 
00237         self.assertGreater(count, 1)
00238         self.assertLess(count, limit)
00239 
00240 unittest.main()

Here is the call graph for this function:


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