Back to index

apport  2.4
test_hookutils.py
Go to the documentation of this file.
00001 # coding: UTF-8
00002 import unittest, tempfile, locale, subprocess, re, shutil, os.path, sys
00003 
00004 import apport.hookutils
00005 
00006 
00007 class T(unittest.TestCase):
00008     def setUp(self):
00009         self.workdir = tempfile.mkdtemp()
00010 
00011     def tearDown(self):
00012         shutil.rmtree(self.workdir)
00013 
00014     def test_module_license_evaluation(self):
00015         '''module licenses can be validated correctly'''
00016 
00017         def _build_ko(license):
00018             asm = tempfile.NamedTemporaryFile(prefix='%s-' % (license),
00019                                               suffix='.S')
00020             asm.write(('.section .modinfo\n.string "license=%s"\n' % (license)).encode())
00021             asm.flush()
00022             ko = tempfile.NamedTemporaryFile(prefix='%s-' % (license),
00023                                              suffix='.ko')
00024             subprocess.call(['/usr/bin/as', asm.name, '-o', ko.name])
00025             return ko
00026 
00027         good_ko = _build_ko('GPL')
00028         bad_ko = _build_ko('BAD')
00029 
00030         # test:
00031         #  - loaded real module
00032         #  - unfindable module
00033         #  - fake GPL module
00034         #  - fake BAD module
00035 
00036         # direct license check
00037         self.assertTrue('GPL' in apport.hookutils._get_module_license('isofs'))
00038         self.assertEqual(apport.hookutils._get_module_license('does-not-exist'), 'invalid')
00039         self.assertTrue('GPL' in apport.hookutils._get_module_license(good_ko.name))
00040         self.assertTrue('BAD' in apport.hookutils._get_module_license(bad_ko.name))
00041 
00042         # check via nonfree_kernel_modules logic
00043         f = tempfile.NamedTemporaryFile()
00044         f.write(('isofs\ndoes-not-exist\n%s\n%s\n' %
00045                 (good_ko.name, bad_ko.name)).encode())
00046         f.flush()
00047         nonfree = apport.hookutils.nonfree_kernel_modules(f.name)
00048         self.assertFalse('isofs' in nonfree)
00049         self.assertTrue('does-not-exist' in nonfree)
00050         self.assertFalse(good_ko.name in nonfree)
00051         self.assertTrue(bad_ko.name in nonfree)
00052 
00053     def test_attach_dmesg(self):
00054         '''attach_dmesg() does not overwrite already existing data'''
00055 
00056         report = {}
00057 
00058         apport.hookutils.attach_dmesg(report)
00059         self.assertTrue(report['BootDmesg'].startswith('['))
00060         self.assertTrue(len(report['BootDmesg']) > 500)
00061         self.assertTrue(report['CurrentDmesg'].startswith('['))
00062 
00063     def test_dmesg_overwrite(self):
00064         '''attach_dmesg() does not overwrite already existing data'''
00065 
00066         report = {'BootDmesg': 'existingboot'}
00067 
00068         apport.hookutils.attach_dmesg(report)
00069         self.assertEqual(report['BootDmesg'][:50], 'existingboot')
00070         self.assertTrue(report['CurrentDmesg'].startswith('['))
00071 
00072         report = {'BootDmesg': 'existingboot', 'CurrentDmesg': 'existingcurrent'}
00073 
00074         apport.hookutils.attach_dmesg(report)
00075         self.assertEqual(report['BootDmesg'], 'existingboot')
00076         self.assertEqual(report['CurrentDmesg'], 'existingcurrent')
00077 
00078     def test_attach_file(self):
00079         '''attach_file()'''
00080 
00081         with open('/etc/motd') as f:
00082             motd_contents = f.read().strip()
00083         with open('/etc/issue') as f:
00084             issue_contents = f.read().strip()
00085 
00086         # default key name
00087         report = {}
00088         apport.hookutils.attach_file(report, '/etc/motd')
00089         self.assertEqual(list(report), ['.etc.motd'])
00090         self.assertEqual(report['.etc.motd'], motd_contents)
00091 
00092         # custom key name
00093         report = {}
00094         apport.hookutils.attach_file(report, '/etc/motd', 'Motd')
00095         self.assertEqual(list(report), ['Motd'])
00096         self.assertEqual(report['Motd'], motd_contents)
00097 
00098         # nonexisting file
00099         report = {}
00100         apport.hookutils.attach_file(report, '/nonexisting')
00101         self.assertEqual(list(report), ['.nonexisting'])
00102         self.assertTrue(report['.nonexisting'].startswith('Error: '))
00103 
00104         # existing key
00105         report = {}
00106         apport.hookutils.attach_file(report, '/etc/motd')
00107         apport.hookutils.attach_file(report, '/etc/motd')
00108         self.assertEqual(list(report), ['.etc.motd'])
00109         self.assertEqual(report['.etc.motd'], motd_contents)
00110 
00111         apport.hookutils.attach_file(report, '/etc/issue', '.etc.motd', overwrite=False)
00112         self.assertEqual(sorted(report.keys()), ['.etc.motd', '.etc.motd_'])
00113         self.assertEqual(report['.etc.motd'], motd_contents)
00114         self.assertEqual(report['.etc.motd_'], issue_contents)
00115 
00116     def test_attach_file_binary(self):
00117         '''attach_file() for binary files'''
00118 
00119         myfile = os.path.join(self.workdir, 'data')
00120         with open(myfile, 'wb') as f:
00121             f.write(b'a\xc3\xb6b\xffx')
00122 
00123         report = {}
00124         apport.hookutils.attach_file(report, myfile, key='data')
00125         self.assertEqual(report['data'], b'a\xc3\xb6b\xffx')
00126 
00127         apport.hookutils.attach_file(report, myfile, key='data', force_unicode=True)
00128         self.assertEqual(report['data'], b'a\xc3\xb6b\xef\xbf\xbdx'.decode('UTF-8'))
00129 
00130     def test_attach_file_if_exists(self):
00131         '''attach_file_if_exists()'''
00132 
00133         with open('/etc/motd') as f:
00134             motd_contents = f.read().strip()
00135 
00136         # default key name
00137         report = {}
00138         apport.hookutils.attach_file_if_exists(report, '/etc/motd')
00139         self.assertEqual(list(report), ['.etc.motd'])
00140         self.assertEqual(report['.etc.motd'], motd_contents)
00141 
00142         # custom key name
00143         report = {}
00144         apport.hookutils.attach_file_if_exists(report, '/etc/motd', 'Motd')
00145         self.assertEqual(list(report), ['Motd'])
00146         self.assertEqual(report['Motd'], motd_contents)
00147 
00148         # nonexisting file
00149         report = {}
00150         apport.hookutils.attach_file_if_exists(report, '/nonexisting')
00151         self.assertEqual(list(report), [])
00152 
00153     def test_recent_logfile(self):
00154         '''recent_logfile'''
00155 
00156         self.assertEqual(apport.hookutils.recent_logfile('/nonexisting', re.compile('.')), '')
00157         self.assertEqual(apport.hookutils.recent_syslog(re.compile('ThisCantPossiblyHitAnything')), '')
00158         self.assertNotEqual(len(apport.hookutils.recent_syslog(re.compile('.'))), 0)
00159 
00160     def test_recent_logfile_overflow(self):
00161         '''recent_logfile on a huge file'''
00162 
00163         log = os.path.join(self.workdir, 'syslog')
00164         with open(log, 'w') as f:
00165             lines = 1000000
00166             while lines >= 0:
00167                 f.write('Apr 20 11:30:00 komputer kernel: bogus message\n')
00168                 lines -= 1
00169 
00170         mem_before = self._get_mem_usage()
00171         data = apport.hookutils.recent_logfile(log, re.compile('kernel'))
00172         mem_after = self._get_mem_usage()
00173         delta_kb = mem_after - mem_before
00174         sys.stderr.write('[Δ %i kB] ' % delta_kb)
00175         self.assertLess(delta_kb, 5000)
00176 
00177         self.assertTrue(data.startswith('Apr 20 11:30:00 komputer kernel: bogus message\n'))
00178         self.assertGreater(len(data), 100000)
00179         self.assertLess(len(data), 1000000)
00180 
00181     @unittest.skipIf(apport.hookutils.apport.hookutils.in_session_of_problem(apport.Report()) is None, 'no ConsoleKit session')
00182     def test_in_session_of_problem(self):
00183         '''in_session_of_problem()'''
00184 
00185         old_ctime = locale.getlocale(locale.LC_TIME)
00186         locale.setlocale(locale.LC_TIME, 'C')
00187 
00188         report = {'Date': 'Sat Jan  1 12:00:00 2011'}
00189         self.assertFalse(apport.hookutils.in_session_of_problem(report))
00190 
00191         report = {'Date': 'Mon Oct 10 21:06:03 2009'}
00192         self.assertFalse(apport.hookutils.in_session_of_problem(report))
00193 
00194         report = {'Date': 'Tue Jan  1 12:00:00 2211'}
00195         self.assertTrue(apport.hookutils.in_session_of_problem(report))
00196 
00197         locale.setlocale(locale.LC_TIME, '')
00198 
00199         report = {'Date': 'Sat Jan  1 12:00:00 2011'}
00200         self.assertFalse(apport.hookutils.in_session_of_problem(report))
00201 
00202         report = {'Date': 'Mon Oct 10 21:06:03 2009'}
00203         self.assertFalse(apport.hookutils.in_session_of_problem(report))
00204 
00205         report = apport.Report()
00206         self.assertTrue(apport.hookutils.in_session_of_problem(report))
00207 
00208         self.assertEqual(apport.hookutils.in_session_of_problem({}), None)
00209 
00210         locale.setlocale(locale.LC_TIME, old_ctime)
00211 
00212     def test_xsession_errors(self):
00213         '''xsession_errors()'''
00214 
00215         with open(os.path.join(self.workdir, '.xsession-errors'), 'w', encoding='UTF-8') as f:
00216             f.write('''Loading profile from /etc/profile
00217 gnome-session[1948]: WARNING: standard glib warning
00218 EggSMClient-CRITICAL **: egg_sm_client_set_mode: standard glib assertion
00219 24/02/2012 11:14:46 Sending credentials s3kr1t
00220 
00221 ** WARNING **: nonstandard warning
00222 
00223 WARN  2012-02-24 11:23:47 unity <unknown>:0 some unicode ♥ ♪
00224 
00225 GNOME_KEYRING_CONTROL=/tmp/keyring-u7hrD6
00226 
00227 (gnome-settings-daemon:5115): Gdk-WARNING **: The program 'gnome-settings-daemon' received an X Window System error.
00228 This probably reflects a bug in the program.
00229 The error was 'BadMatch (invalid parameter attributes)'.
00230   (Details: serial 723 error_code 8 request_code 143 minor_code 22)
00231   (Note to programmers: normally, X errors are reported asynchronously;
00232    that is, you will receive the error a while after causing it.
00233    To debug your program, run it with the --sync command line
00234    option to change this behavior. You can then get a meaningful
00235    backtrace from your debugger if you break on the gdk_x_error() function.)"
00236 
00237 GdkPixbuf-CRITICAL **: gdk_pixbuf_scale_simple: another standard glib assertion
00238 ''')
00239         orig_home = os.environ.get('HOME')
00240         try:
00241             os.environ['HOME'] = self.workdir
00242 
00243             # explicit pattern
00244             pattern = re.compile('notfound')
00245             self.assertEqual(apport.hookutils.xsession_errors(pattern), '')
00246 
00247             pattern = re.compile('^\w+-CRITICAL')
00248             res = apport.hookutils.xsession_errors(pattern).splitlines()
00249             self.assertEqual(len(res), 2)
00250             self.assertTrue(res[0].startswith('EggSMClient-CRITICAL'))
00251             self.assertTrue(res[1].startswith('GdkPixbuf-CRITICAL'))
00252 
00253             # default pattern includes glib assertions and X Errors
00254             res = apport.hookutils.xsession_errors()
00255             self.assertFalse('nonstandard warning' in res)
00256             self.assertFalse('keyring' in res)
00257             self.assertFalse('credentials' in res)
00258             self.assertTrue('WARNING: standard glib warning' in res, res)
00259             self.assertTrue('GdkPixbuf-CRITICAL' in res, res)
00260             self.assertTrue("'gnome-settings-daemon' received an X Window" in res, res)
00261             self.assertTrue('BadMatch' in res, res)
00262             self.assertTrue('serial 723' in res, res)
00263 
00264         finally:
00265             if orig_home is not None:
00266                 os.environ['HOME'] = orig_home
00267             else:
00268                 os.unsetenv('HOME')
00269 
00270     def test_no_crashes(self):
00271         '''functions do not crash (very shallow)'''
00272 
00273         report = {}
00274         apport.hookutils.attach_hardware(report)
00275         apport.hookutils.attach_alsa(report)
00276         apport.hookutils.attach_network(report)
00277         apport.hookutils.attach_wifi(report)
00278         apport.hookutils.attach_printing(report)
00279         apport.hookutils.attach_conffiles(report, 'bash')
00280         apport.hookutils.attach_conffiles(report, 'apport')
00281         apport.hookutils.attach_conffiles(report, 'nonexisting')
00282         apport.hookutils.attach_upstart_overrides(report, 'apport')
00283         apport.hookutils.attach_upstart_overrides(report, 'nonexisting')
00284         apport.hookutils.attach_default_grub(report)
00285 
00286     def test_command_output(self):
00287         orig_lcm = os.environ.get('LC_MESSAGES')
00288         os.environ['LC_MESSAGES'] = 'en_US.UTF-8'
00289         try:
00290             # default mode: disable translations
00291             out = apport.hookutils.command_output(['env'])
00292             self.assertTrue('LC_MESSAGES=C' in out)
00293 
00294             # keep locale
00295             out = apport.hookutils.command_output(['env'], keep_locale=True)
00296             self.assertFalse('LC_MESSAGES=C' in out, out)
00297         finally:
00298             if orig_lcm is not None:
00299                 os.environ['LC_MESSAGES'] = orig_lcm
00300             else:
00301                 os.unsetenv('LC_MESSAGES')
00302 
00303         # nonexisting binary
00304         out = apport.hookutils.command_output(['/non existing'])
00305         self.assertTrue(out.startswith('Error: [Errno 2]'))
00306 
00307         # stdin
00308         out = apport.hookutils.command_output(['cat'], input=b'hello')
00309         self.assertEqual(out, 'hello')
00310 
00311     @classmethod
00312     def _get_mem_usage(klass):
00313         '''Get current memory usage in kB'''
00314 
00315         with open('/proc/self/status') as f:
00316             for line in f:
00317                 if line.startswith('VmSize:'):
00318                     return int(line.split()[1])
00319             else:
00320                 raise SystemError('did not find VmSize: in /proc/self/status')
00321 
00322 unittest.main()