Back to index

apport  2.3
test_hooks.py
Go to the documentation of this file.
00001 '''Test the various package hooks.'''
00002 
00003 # Copyright (C) 2007 - 2009 Canonical Ltd.
00004 # Author: Martin Pitt <martin.pitt@ubuntu.com>
00005 #
00006 # This program is free software; you can redistribute it and/or modify it
00007 # under the terms of the GNU General Public License as published by the
00008 # Free Software Foundation; either version 2 of the License, or (at your
00009 # option) any later version.  See http://www.gnu.org/copyleft/gpl.html for
00010 # the full text of the license.
00011 
00012 import unittest, subprocess, tempfile, os, shutil, os.path, sys, optparse
00013 
00014 import apport, apport.fileutils
00015 
00016 # parse command line options
00017 optparser = optparse.OptionParser('%prog [options]')
00018 
00019 datadir = os.environ.get('APPORT_DATA_DIR', '/usr/share/apport')
00020 
00021 
00022 class T(unittest.TestCase):
00023     def setUp(self):
00024         self.orig_report_dir = apport.fileutils.report_dir
00025         apport.fileutils.report_dir = tempfile.mkdtemp()
00026         os.environ['APPORT_REPORT_DIR'] = apport.fileutils.report_dir
00027 
00028         self.workdir = tempfile.mkdtemp()
00029 
00030     def tearDown(self):
00031         shutil.rmtree(apport.fileutils.report_dir)
00032         apport.fileutils.report_dir = self.orig_report_dir
00033 
00034         shutil.rmtree(self.workdir)
00035 
00036     def test_package_hook_nologs(self):
00037         '''package_hook without any log files.'''
00038 
00039         ph = subprocess.Popen(['%s/package_hook' % datadir, '-p', 'bash'],
00040                               stdin=subprocess.PIPE)
00041         ph.communicate(b'something is wrong')
00042         self.assertEqual(ph.returncode, 0, 'package_hook finished successfully')
00043 
00044         reps = apport.fileutils.get_new_reports()
00045         self.assertEqual(len(reps), 1, 'package_hook created a report')
00046 
00047         r = apport.Report()
00048         with open(reps[0], 'rb') as f:
00049             r.load(f)
00050 
00051         self.assertEqual(r['ProblemType'], 'Package')
00052         self.assertEqual(r['Package'], 'bash')
00053         self.assertEqual(r['SourcePackage'], 'bash')
00054         self.assertEqual(r['ErrorMessage'], 'something is wrong')
00055 
00056     def test_package_hook_uninstalled(self):
00057         '''package_hook on an uninstalled package (might fail to install).'''
00058 
00059         pkg = apport.packaging.get_uninstalled_package()
00060         ph = subprocess.Popen(['%s/package_hook' % datadir, '-p', pkg],
00061                               stdin=subprocess.PIPE)
00062         ph.communicate(b'something is wrong')
00063         self.assertEqual(ph.returncode, 0, 'package_hook finished successfully')
00064 
00065         reps = apport.fileutils.get_new_reports()
00066         self.assertEqual(len(reps), 1, 'package_hook created a report')
00067 
00068         r = apport.Report()
00069         with open(reps[0], 'rb') as f:
00070             r.load(f)
00071 
00072         self.assertEqual(r['ProblemType'], 'Package')
00073         self.assertEqual(r['Package'], pkg)
00074         self.assertEqual(r['SourcePackage'], apport.packaging.get_source(pkg))
00075         self.assertEqual(r['ErrorMessage'], 'something is wrong')
00076 
00077     def test_package_hook_logs(self):
00078         '''package_hook with a log dir and a log file.'''
00079 
00080         with open(os.path.join(self.workdir, 'log_1.log'), 'w') as f:
00081             f.write('Log 1\nbla')
00082         with open(os.path.join(self.workdir, 'log2'), 'w') as f:
00083             f.write('Yet\nanother\nlog')
00084         os.mkdir(os.path.join(self.workdir, 'logsub'))
00085         with open(os.path.join(self.workdir, 'logsub', 'notme.log'), 'w') as f:
00086             f.write('not me!')
00087 
00088         ph = subprocess.Popen(['%s/package_hook' % datadir, '-p', 'bash', '-l',
00089                                os.path.realpath(sys.argv[0]), '-l', self.workdir],
00090                               stdin=subprocess.PIPE)
00091         ph.communicate(b'something is wrong')
00092         self.assertEqual(ph.returncode, 0, 'package_hook finished successfully')
00093 
00094         reps = apport.fileutils.get_new_reports()
00095         self.assertEqual(len(reps), 1, 'package_hook created a report')
00096 
00097         r = apport.Report()
00098         with open(reps[0], 'rb') as f:
00099             r.load(f)
00100 
00101         filekey = None
00102         log1key = None
00103         log2key = None
00104         for k in r.keys():
00105             if k.endswith('Testhookspy'):
00106                 filekey = k
00107             elif k.endswith('Log1log'):
00108                 log1key = k
00109             elif k.endswith('Log2'):
00110                 log2key = k
00111             elif 'sub' in k:
00112                 self.fail('logsub should not go into log files')
00113 
00114         self.assertTrue(filekey)
00115         self.assertTrue(log1key)
00116         self.assertTrue(log2key)
00117         self.assertTrue('0234lkjas' in r[filekey])
00118         self.assertEqual(len(r[filekey]), os.path.getsize(sys.argv[0]))
00119         self.assertEqual(r[log1key], 'Log 1\nbla')
00120         self.assertEqual(r[log2key], 'Yet\nanother\nlog')
00121 
00122     def test_package_hook_tags(self):
00123         '''package_hook with extra tags argument.'''
00124 
00125         ph = subprocess.Popen(['%s/package_hook' % datadir, '-p', 'bash',
00126                                '-t', 'dist-upgrade, verybad'], stdin=subprocess.PIPE)
00127         ph.communicate(b'something is wrong')
00128         self.assertEqual(ph.returncode, 0, 'package_hook finished successfully')
00129 
00130         reps = apport.fileutils.get_new_reports()
00131         self.assertEqual(len(reps), 1, 'package_hook created a report')
00132 
00133         r = apport.Report()
00134         with open(reps[0], 'rb') as f:
00135             r.load(f)
00136 
00137         self.assertEqual(r['Tags'], 'dist-upgrade verybad')
00138 
00139     def test_kernel_crashdump(self):
00140         '''kernel_crashdump.'''
00141 
00142         f = open(os.path.join(apport.fileutils.report_dir, 'vmcore'), 'wb')
00143         f.write(b'\x01' * 100)
00144         f.close()
00145         f = open(os.path.join(apport.fileutils.report_dir, 'vmcore.log'), 'w')
00146         f.write('vmcore successfully dumped')
00147         f.close()
00148 
00149         self.assertEqual(subprocess.call('%s/kernel_crashdump' % datadir), 0,
00150                          'kernel_crashdump finished successfully')
00151 
00152         reps = apport.fileutils.get_new_reports()
00153         self.assertEqual(len(reps), 1, 'kernel_crashdump created a report')
00154 
00155         r = apport.Report()
00156         with open(reps[0], 'rb') as f:
00157             r.load(f)
00158 
00159         self.assertEqual(set(r.keys()), set(['Date', 'Package', 'ProblemType',
00160                                              'VmCore', 'VmCoreLog', 'Uname',
00161                                              'Architecture', 'DistroRelease']))
00162         self.assertEqual(r['ProblemType'], 'KernelCrash')
00163         self.assertEqual(r['VmCoreLog'], 'vmcore successfully dumped')
00164         self.assertEqual(r['VmCore'], b'\x01' * 100)
00165         self.assertTrue('linux' in r['Package'])
00166 
00167         self.assertTrue(os.uname()[2].split('-')[0] in r['Package'])
00168 
00169         r.add_package_info(r['Package'])
00170         self.assertTrue(' ' in r['Package'])  # appended version number
00171 
00172     @classmethod
00173     def _gcc_version_path(klass):
00174         '''Determine a valid version and executable path of gcc and return it
00175         as a tuple.'''
00176 
00177         gcc = subprocess.Popen(['gcc', '--version'], stdout=subprocess.PIPE)
00178         out = gcc.communicate()[0].decode()
00179         assert gcc.returncode == 0, '"gcc --version" must work for this test suite'
00180 
00181         gcc_ver = '.'.join(out.splitlines()[0].split()[2].split('.')[:2])
00182         gcc_path = '/usr/bin/gcc-' + gcc_ver
00183 
00184         assert subprocess.call([gcc_path, '--version'], stdout=subprocess.PIPE) == 0, \
00185             gcc_path + ' must exist and work for this test suite'
00186 
00187         return (gcc_ver, gcc_path)
00188 
00189     def test_gcc_ide_hook_file(self):
00190         '''gcc_ice_hook with a temporary file.'''
00191 
00192         (gcc_version, gcc_path) = self._gcc_version_path()
00193 
00194         test_source = tempfile.NamedTemporaryFile()
00195         test_source.write(b'int f(int x);')
00196         test_source.flush()
00197         test_source.seek(0)
00198 
00199         self.assertEqual(subprocess.call(['%s/gcc_ice_hook' % datadir, gcc_path, test_source.name]),
00200                          0, 'gcc_ice_hook finished successfully')
00201 
00202         reps = apport.fileutils.get_new_reports()
00203         self.assertEqual(len(reps), 1, 'gcc_ice_hook created a report')
00204 
00205         r = apport.Report()
00206         with open(reps[0], 'rb') as f:
00207             r.load(f)
00208         self.assertEqual(r['ProblemType'], 'Crash')
00209         self.assertEqual(r['ExecutablePath'], gcc_path)
00210         self.assertEqual(r['PreprocessedSource'], test_source.read().decode())
00211 
00212         r.add_package_info()
00213 
00214         self.assertEqual(r['Package'].split()[0], 'gcc-' + gcc_version)
00215         self.assertTrue(r['SourcePackage'].startswith('gcc'))
00216 
00217     def test_gcc_ide_hook_pipe(self):
00218         '''gcc_ice_hook with piping.'''
00219 
00220         (gcc_version, gcc_path) = self._gcc_version_path()
00221 
00222         test_source = 'int f(int x);'
00223 
00224         hook = subprocess.Popen(['%s/gcc_ice_hook' % datadir, gcc_path, '-'],
00225                                 stdin=subprocess.PIPE)
00226         hook.communicate(test_source.encode())
00227         self.assertEqual(hook.returncode, 0, 'gcc_ice_hook finished successfully')
00228 
00229         reps = apport.fileutils.get_new_reports()
00230         self.assertEqual(len(reps), 1, 'gcc_ice_hook created a report')
00231 
00232         r = apport.Report()
00233         with open(reps[0], 'rb') as f:
00234             r.load(f)
00235 
00236         self.assertEqual(r['ProblemType'], 'Crash')
00237         self.assertEqual(r['ExecutablePath'], gcc_path)
00238         self.assertEqual(r['PreprocessedSource'], test_source)
00239 
00240         r.add_package_info()
00241 
00242         self.assertEqual(r['Package'].split()[0], 'gcc-' + gcc_version)
00243         self.assertTrue(r['SourcePackage'].startswith('gcc'))
00244 
00245     def test_kernel_oops_hook(self):
00246         test_source = '''------------[ cut here ]------------
00247 kernel BUG at /tmp/oops.c:5!
00248 invalid opcode: 0000 [#1] SMP
00249 Modules linked in: oops cpufreq_stats ext2 i915 drm nf_conntrack_ipv4 ipt_REJECT iptable_filter ip_tables nf_conntrack_ipv6 xt_state nf_conntrack xt_tcpudp ip6t_ipv6header ip6t_REJECT ip6table_filter ip6_tables x_tables ipv6 loop dm_multipath rtc_cmos iTCO_wdt iTCO_vendor_support pcspkr i2c_i801 i2c_core battery video ac output power_supply button sg joydev usb_storage dm_snapshot dm_zero dm_mirror dm_mod ahci pata_acpi ata_generic ata_piix libata sd_mod scsi_mod ext3 jbd mbcache uhci_hcd ohci_hcd ehci_hcd
00250 '''
00251         hook = subprocess.Popen(['%s/kernel_oops' % datadir], stdin=subprocess.PIPE)
00252         hook.communicate(test_source.encode())
00253         self.assertEqual(hook.returncode, 0, 'kernel_oops finished successfully')
00254 
00255         reps = apport.fileutils.get_new_reports()
00256         self.assertEqual(len(reps), 1, 'kernel_oops created a report')
00257 
00258         r = apport.Report()
00259         with open(reps[0], 'rb') as f:
00260             r.load(f)
00261 
00262         self.assertEqual(r['ProblemType'], 'KernelOops')
00263         self.assertEqual(r['OopsText'], test_source)
00264 
00265         r.add_package_info(r['Package'])
00266 
00267         self.assertTrue(r['Package'].startswith('linux-image-'))
00268 
00269 unittest.main()