1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/testing/mozbase/mozcrash/tests/test.py Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,218 @@ 1.4 +#!/usr/bin/env python 1.5 +# 1.6 +# This Source Code Form is subject to the terms of the Mozilla Public 1.7 +# License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.8 +# You can obtain one at http://mozilla.org/MPL/2.0/. 1.9 + 1.10 +import os, unittest, subprocess, tempfile, shutil, urlparse, zipfile, StringIO 1.11 +import mozcrash, mozlog, mozhttpd 1.12 + 1.13 +# Make logs go away 1.14 +log = mozlog.getLogger("mozcrash", handler=mozlog.FileHandler(os.devnull)) 1.15 + 1.16 +def popen_factory(stdouts): 1.17 + """ 1.18 + Generate a class that can mock subprocess.Popen. |stdouts| is an iterable that 1.19 + should return an iterable for the stdout of each process in turn. 1.20 + """ 1.21 + class mock_popen(object): 1.22 + def __init__(self, args, *args_rest, **kwargs): 1.23 + self.stdout = stdouts.next() 1.24 + self.returncode = 0 1.25 + 1.26 + def wait(self): 1.27 + return 0 1.28 + 1.29 + def communicate(self): 1.30 + return (self.stdout.next(), "") 1.31 + 1.32 + return mock_popen 1.33 + 1.34 +class TestCrash(unittest.TestCase): 1.35 + def setUp(self): 1.36 + self.tempdir = tempfile.mkdtemp() 1.37 + # a fake file to use as a stackwalk binary 1.38 + self.stackwalk = os.path.join(self.tempdir, "stackwalk") 1.39 + open(self.stackwalk, "w").write("fake binary") 1.40 + self._subprocess_popen = subprocess.Popen 1.41 + subprocess.Popen = popen_factory(self.next_mock_stdout()) 1.42 + self.stdouts = [] 1.43 + 1.44 + def tearDown(self): 1.45 + subprocess.Popen = self._subprocess_popen 1.46 + shutil.rmtree(self.tempdir) 1.47 + 1.48 + def next_mock_stdout(self): 1.49 + if not self.stdouts: 1.50 + yield iter([]) 1.51 + for s in self.stdouts: 1.52 + yield iter(s) 1.53 + 1.54 + def test_nodumps(self): 1.55 + """ 1.56 + Test that check_for_crashes returns False if no dumps are present. 1.57 + """ 1.58 + self.stdouts.append(["this is some output"]) 1.59 + self.assertFalse(mozcrash.check_for_crashes(self.tempdir, 1.60 + 'symbols_path', 1.61 + stackwalk_binary=self.stackwalk, 1.62 + quiet=True)) 1.63 + 1.64 + def test_simple(self): 1.65 + """ 1.66 + Test that check_for_crashes returns True if a dump is present. 1.67 + """ 1.68 + open(os.path.join(self.tempdir, "test.dmp"), "w").write("foo") 1.69 + self.stdouts.append(["this is some output"]) 1.70 + self.assert_(mozcrash.check_for_crashes(self.tempdir, 1.71 + 'symbols_path', 1.72 + stackwalk_binary=self.stackwalk, 1.73 + quiet=True)) 1.74 + 1.75 + def test_stackwalk_envvar(self): 1.76 + """ 1.77 + Test that check_for_crashes uses the MINIDUMP_STACKWALK environment var. 1.78 + """ 1.79 + open(os.path.join(self.tempdir, "test.dmp"), "w").write("foo") 1.80 + self.stdouts.append(["this is some output"]) 1.81 + os.environ['MINIDUMP_STACKWALK'] = self.stackwalk 1.82 + self.assert_(mozcrash.check_for_crashes(self.tempdir, 1.83 + 'symbols_path', 1.84 + quiet=True)) 1.85 + del os.environ['MINIDUMP_STACKWALK'] 1.86 + 1.87 + def test_save_path(self): 1.88 + """ 1.89 + Test that dump_save_path works. 1.90 + """ 1.91 + open(os.path.join(self.tempdir, "test.dmp"), "w").write("foo") 1.92 + open(os.path.join(self.tempdir, "test.extra"), "w").write("bar") 1.93 + save_path = os.path.join(self.tempdir, "saved") 1.94 + os.mkdir(save_path) 1.95 + self.stdouts.append(["this is some output"]) 1.96 + self.assert_(mozcrash.check_for_crashes(self.tempdir, 1.97 + 'symbols_path', 1.98 + stackwalk_binary=self.stackwalk, 1.99 + dump_save_path=save_path, 1.100 + quiet=True)) 1.101 + self.assert_(os.path.isfile(os.path.join(save_path, "test.dmp"))) 1.102 + self.assert_(os.path.isfile(os.path.join(save_path, "test.extra"))) 1.103 + 1.104 + def test_save_path_not_present(self): 1.105 + """ 1.106 + Test that dump_save_path works when the directory doesn't exist. 1.107 + """ 1.108 + open(os.path.join(self.tempdir, "test.dmp"), "w").write("foo") 1.109 + open(os.path.join(self.tempdir, "test.extra"), "w").write("bar") 1.110 + save_path = os.path.join(self.tempdir, "saved") 1.111 + self.stdouts.append(["this is some output"]) 1.112 + self.assert_(mozcrash.check_for_crashes(self.tempdir, 1.113 + 'symbols_path', 1.114 + stackwalk_binary=self.stackwalk, 1.115 + dump_save_path=save_path, 1.116 + quiet=True)) 1.117 + self.assert_(os.path.isfile(os.path.join(save_path, "test.dmp"))) 1.118 + self.assert_(os.path.isfile(os.path.join(save_path, "test.extra"))) 1.119 + 1.120 + def test_save_path_isfile(self): 1.121 + """ 1.122 + Test that dump_save_path works when the directory doesn't exist, 1.123 + but a file with the same name exists. 1.124 + """ 1.125 + open(os.path.join(self.tempdir, "test.dmp"), "w").write("foo") 1.126 + open(os.path.join(self.tempdir, "test.extra"), "w").write("bar") 1.127 + save_path = os.path.join(self.tempdir, "saved") 1.128 + open(save_path, "w").write("junk") 1.129 + self.stdouts.append(["this is some output"]) 1.130 + self.assert_(mozcrash.check_for_crashes(self.tempdir, 1.131 + 'symbols_path', 1.132 + stackwalk_binary=self.stackwalk, 1.133 + dump_save_path=save_path, 1.134 + quiet=True)) 1.135 + self.assert_(os.path.isfile(os.path.join(save_path, "test.dmp"))) 1.136 + self.assert_(os.path.isfile(os.path.join(save_path, "test.extra"))) 1.137 + 1.138 + def test_save_path_envvar(self): 1.139 + """ 1.140 + Test that the MINDUMP_SAVE_PATH environment variable works. 1.141 + """ 1.142 + open(os.path.join(self.tempdir, "test.dmp"), "w").write("foo") 1.143 + open(os.path.join(self.tempdir, "test.extra"), "w").write("bar") 1.144 + save_path = os.path.join(self.tempdir, "saved") 1.145 + os.mkdir(save_path) 1.146 + self.stdouts.append(["this is some output"]) 1.147 + os.environ['MINIDUMP_SAVE_PATH'] = save_path 1.148 + self.assert_(mozcrash.check_for_crashes(self.tempdir, 1.149 + 'symbols_path', 1.150 + stackwalk_binary=self.stackwalk, 1.151 + quiet=True)) 1.152 + del os.environ['MINIDUMP_SAVE_PATH'] 1.153 + self.assert_(os.path.isfile(os.path.join(save_path, "test.dmp"))) 1.154 + self.assert_(os.path.isfile(os.path.join(save_path, "test.extra"))) 1.155 + 1.156 + def test_symbol_path_url(self): 1.157 + """ 1.158 + Test that passing a URL as symbols_path correctly fetches the URL. 1.159 + """ 1.160 + open(os.path.join(self.tempdir, "test.dmp"), "w").write("foo") 1.161 + self.stdouts.append(["this is some output"]) 1.162 + 1.163 + def make_zipfile(): 1.164 + data = StringIO.StringIO() 1.165 + z = zipfile.ZipFile(data, 'w') 1.166 + z.writestr("symbols.txt", "abc/xyz") 1.167 + z.close() 1.168 + return data.getvalue() 1.169 + def get_symbols(req): 1.170 + headers = {} 1.171 + return (200, headers, make_zipfile()) 1.172 + httpd = mozhttpd.MozHttpd(port=0, 1.173 + urlhandlers=[{'method':'GET', 'path':'/symbols', 'function':get_symbols}]) 1.174 + httpd.start() 1.175 + symbol_url = urlparse.urlunsplit(('http', '%s:%d' % httpd.httpd.server_address, 1.176 + '/symbols','','')) 1.177 + self.assert_(mozcrash.check_for_crashes(self.tempdir, 1.178 + symbol_url, 1.179 + stackwalk_binary=self.stackwalk, 1.180 + quiet=True)) 1.181 + 1.182 +class TestJavaException(unittest.TestCase): 1.183 + def setUp(self): 1.184 + self.test_log = ["01-30 20:15:41.937 E/GeckoAppShell( 1703): >>> REPORTING UNCAUGHT EXCEPTION FROM THREAD 9 (\"GeckoBackgroundThread\")", 1.185 + "01-30 20:15:41.937 E/GeckoAppShell( 1703): java.lang.NullPointerException", 1.186 + "01-30 20:15:41.937 E/GeckoAppShell( 1703): at org.mozilla.gecko.GeckoApp$21.run(GeckoApp.java:1833)", 1.187 + "01-30 20:15:41.937 E/GeckoAppShell( 1703): at android.os.Handler.handleCallback(Handler.java:587)"] 1.188 + 1.189 + def test_uncaught_exception(self): 1.190 + """ 1.191 + Test for an exception which should be caught 1.192 + """ 1.193 + self.assert_(mozcrash.check_for_java_exception(self.test_log, quiet=True)) 1.194 + 1.195 + def test_fatal_exception(self): 1.196 + """ 1.197 + Test for an exception which should be caught 1.198 + """ 1.199 + fatal_log = list(self.test_log) 1.200 + fatal_log[0] = "01-30 20:15:41.937 E/GeckoAppShell( 1703): >>> FATAL EXCEPTION FROM THREAD 9 (\"GeckoBackgroundThread\")" 1.201 + self.assert_(mozcrash.check_for_java_exception(fatal_log, quiet=True)) 1.202 + 1.203 + def test_truncated_exception(self): 1.204 + """ 1.205 + Test for an exception which should be caught which 1.206 + was truncated 1.207 + """ 1.208 + truncated_log = list(self.test_log) 1.209 + truncated_log[0], truncated_log[1] = truncated_log[1], truncated_log[0] 1.210 + self.assert_(mozcrash.check_for_java_exception(truncated_log, quiet=True)) 1.211 + 1.212 + def test_unchecked_exception(self): 1.213 + """ 1.214 + Test for an exception which should not be caught 1.215 + """ 1.216 + passable_log = list(self.test_log) 1.217 + passable_log[0] = "01-30 20:15:41.937 E/GeckoAppShell( 1703): >>> NOT-SO-BAD EXCEPTION FROM THREAD 9 (\"GeckoBackgroundThread\")" 1.218 + self.assert_(not mozcrash.check_for_java_exception(passable_log, quiet=True)) 1.219 + 1.220 +if __name__ == '__main__': 1.221 + unittest.main()