Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | #!/usr/bin/env python |
michael@0 | 2 | # |
michael@0 | 3 | # This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this file, |
michael@0 | 5 | # You can obtain one at http://mozilla.org/MPL/2.0/. |
michael@0 | 6 | |
michael@0 | 7 | import os, unittest, subprocess, tempfile, shutil, urlparse, zipfile, StringIO |
michael@0 | 8 | import mozcrash, mozlog, mozhttpd |
michael@0 | 9 | |
michael@0 | 10 | # Make logs go away |
michael@0 | 11 | log = mozlog.getLogger("mozcrash", handler=mozlog.FileHandler(os.devnull)) |
michael@0 | 12 | |
michael@0 | 13 | def popen_factory(stdouts): |
michael@0 | 14 | """ |
michael@0 | 15 | Generate a class that can mock subprocess.Popen. |stdouts| is an iterable that |
michael@0 | 16 | should return an iterable for the stdout of each process in turn. |
michael@0 | 17 | """ |
michael@0 | 18 | class mock_popen(object): |
michael@0 | 19 | def __init__(self, args, *args_rest, **kwargs): |
michael@0 | 20 | self.stdout = stdouts.next() |
michael@0 | 21 | self.returncode = 0 |
michael@0 | 22 | |
michael@0 | 23 | def wait(self): |
michael@0 | 24 | return 0 |
michael@0 | 25 | |
michael@0 | 26 | def communicate(self): |
michael@0 | 27 | return (self.stdout.next(), "") |
michael@0 | 28 | |
michael@0 | 29 | return mock_popen |
michael@0 | 30 | |
michael@0 | 31 | class TestCrash(unittest.TestCase): |
michael@0 | 32 | def setUp(self): |
michael@0 | 33 | self.tempdir = tempfile.mkdtemp() |
michael@0 | 34 | # a fake file to use as a stackwalk binary |
michael@0 | 35 | self.stackwalk = os.path.join(self.tempdir, "stackwalk") |
michael@0 | 36 | open(self.stackwalk, "w").write("fake binary") |
michael@0 | 37 | self._subprocess_popen = subprocess.Popen |
michael@0 | 38 | subprocess.Popen = popen_factory(self.next_mock_stdout()) |
michael@0 | 39 | self.stdouts = [] |
michael@0 | 40 | |
michael@0 | 41 | def tearDown(self): |
michael@0 | 42 | subprocess.Popen = self._subprocess_popen |
michael@0 | 43 | shutil.rmtree(self.tempdir) |
michael@0 | 44 | |
michael@0 | 45 | def next_mock_stdout(self): |
michael@0 | 46 | if not self.stdouts: |
michael@0 | 47 | yield iter([]) |
michael@0 | 48 | for s in self.stdouts: |
michael@0 | 49 | yield iter(s) |
michael@0 | 50 | |
michael@0 | 51 | def test_nodumps(self): |
michael@0 | 52 | """ |
michael@0 | 53 | Test that check_for_crashes returns False if no dumps are present. |
michael@0 | 54 | """ |
michael@0 | 55 | self.stdouts.append(["this is some output"]) |
michael@0 | 56 | self.assertFalse(mozcrash.check_for_crashes(self.tempdir, |
michael@0 | 57 | 'symbols_path', |
michael@0 | 58 | stackwalk_binary=self.stackwalk, |
michael@0 | 59 | quiet=True)) |
michael@0 | 60 | |
michael@0 | 61 | def test_simple(self): |
michael@0 | 62 | """ |
michael@0 | 63 | Test that check_for_crashes returns True if a dump is present. |
michael@0 | 64 | """ |
michael@0 | 65 | open(os.path.join(self.tempdir, "test.dmp"), "w").write("foo") |
michael@0 | 66 | self.stdouts.append(["this is some output"]) |
michael@0 | 67 | self.assert_(mozcrash.check_for_crashes(self.tempdir, |
michael@0 | 68 | 'symbols_path', |
michael@0 | 69 | stackwalk_binary=self.stackwalk, |
michael@0 | 70 | quiet=True)) |
michael@0 | 71 | |
michael@0 | 72 | def test_stackwalk_envvar(self): |
michael@0 | 73 | """ |
michael@0 | 74 | Test that check_for_crashes uses the MINIDUMP_STACKWALK environment var. |
michael@0 | 75 | """ |
michael@0 | 76 | open(os.path.join(self.tempdir, "test.dmp"), "w").write("foo") |
michael@0 | 77 | self.stdouts.append(["this is some output"]) |
michael@0 | 78 | os.environ['MINIDUMP_STACKWALK'] = self.stackwalk |
michael@0 | 79 | self.assert_(mozcrash.check_for_crashes(self.tempdir, |
michael@0 | 80 | 'symbols_path', |
michael@0 | 81 | quiet=True)) |
michael@0 | 82 | del os.environ['MINIDUMP_STACKWALK'] |
michael@0 | 83 | |
michael@0 | 84 | def test_save_path(self): |
michael@0 | 85 | """ |
michael@0 | 86 | Test that dump_save_path works. |
michael@0 | 87 | """ |
michael@0 | 88 | open(os.path.join(self.tempdir, "test.dmp"), "w").write("foo") |
michael@0 | 89 | open(os.path.join(self.tempdir, "test.extra"), "w").write("bar") |
michael@0 | 90 | save_path = os.path.join(self.tempdir, "saved") |
michael@0 | 91 | os.mkdir(save_path) |
michael@0 | 92 | self.stdouts.append(["this is some output"]) |
michael@0 | 93 | self.assert_(mozcrash.check_for_crashes(self.tempdir, |
michael@0 | 94 | 'symbols_path', |
michael@0 | 95 | stackwalk_binary=self.stackwalk, |
michael@0 | 96 | dump_save_path=save_path, |
michael@0 | 97 | quiet=True)) |
michael@0 | 98 | self.assert_(os.path.isfile(os.path.join(save_path, "test.dmp"))) |
michael@0 | 99 | self.assert_(os.path.isfile(os.path.join(save_path, "test.extra"))) |
michael@0 | 100 | |
michael@0 | 101 | def test_save_path_not_present(self): |
michael@0 | 102 | """ |
michael@0 | 103 | Test that dump_save_path works when the directory doesn't exist. |
michael@0 | 104 | """ |
michael@0 | 105 | open(os.path.join(self.tempdir, "test.dmp"), "w").write("foo") |
michael@0 | 106 | open(os.path.join(self.tempdir, "test.extra"), "w").write("bar") |
michael@0 | 107 | save_path = os.path.join(self.tempdir, "saved") |
michael@0 | 108 | self.stdouts.append(["this is some output"]) |
michael@0 | 109 | self.assert_(mozcrash.check_for_crashes(self.tempdir, |
michael@0 | 110 | 'symbols_path', |
michael@0 | 111 | stackwalk_binary=self.stackwalk, |
michael@0 | 112 | dump_save_path=save_path, |
michael@0 | 113 | quiet=True)) |
michael@0 | 114 | self.assert_(os.path.isfile(os.path.join(save_path, "test.dmp"))) |
michael@0 | 115 | self.assert_(os.path.isfile(os.path.join(save_path, "test.extra"))) |
michael@0 | 116 | |
michael@0 | 117 | def test_save_path_isfile(self): |
michael@0 | 118 | """ |
michael@0 | 119 | Test that dump_save_path works when the directory doesn't exist, |
michael@0 | 120 | but a file with the same name exists. |
michael@0 | 121 | """ |
michael@0 | 122 | open(os.path.join(self.tempdir, "test.dmp"), "w").write("foo") |
michael@0 | 123 | open(os.path.join(self.tempdir, "test.extra"), "w").write("bar") |
michael@0 | 124 | save_path = os.path.join(self.tempdir, "saved") |
michael@0 | 125 | open(save_path, "w").write("junk") |
michael@0 | 126 | self.stdouts.append(["this is some output"]) |
michael@0 | 127 | self.assert_(mozcrash.check_for_crashes(self.tempdir, |
michael@0 | 128 | 'symbols_path', |
michael@0 | 129 | stackwalk_binary=self.stackwalk, |
michael@0 | 130 | dump_save_path=save_path, |
michael@0 | 131 | quiet=True)) |
michael@0 | 132 | self.assert_(os.path.isfile(os.path.join(save_path, "test.dmp"))) |
michael@0 | 133 | self.assert_(os.path.isfile(os.path.join(save_path, "test.extra"))) |
michael@0 | 134 | |
michael@0 | 135 | def test_save_path_envvar(self): |
michael@0 | 136 | """ |
michael@0 | 137 | Test that the MINDUMP_SAVE_PATH environment variable works. |
michael@0 | 138 | """ |
michael@0 | 139 | open(os.path.join(self.tempdir, "test.dmp"), "w").write("foo") |
michael@0 | 140 | open(os.path.join(self.tempdir, "test.extra"), "w").write("bar") |
michael@0 | 141 | save_path = os.path.join(self.tempdir, "saved") |
michael@0 | 142 | os.mkdir(save_path) |
michael@0 | 143 | self.stdouts.append(["this is some output"]) |
michael@0 | 144 | os.environ['MINIDUMP_SAVE_PATH'] = save_path |
michael@0 | 145 | self.assert_(mozcrash.check_for_crashes(self.tempdir, |
michael@0 | 146 | 'symbols_path', |
michael@0 | 147 | stackwalk_binary=self.stackwalk, |
michael@0 | 148 | quiet=True)) |
michael@0 | 149 | del os.environ['MINIDUMP_SAVE_PATH'] |
michael@0 | 150 | self.assert_(os.path.isfile(os.path.join(save_path, "test.dmp"))) |
michael@0 | 151 | self.assert_(os.path.isfile(os.path.join(save_path, "test.extra"))) |
michael@0 | 152 | |
michael@0 | 153 | def test_symbol_path_url(self): |
michael@0 | 154 | """ |
michael@0 | 155 | Test that passing a URL as symbols_path correctly fetches the URL. |
michael@0 | 156 | """ |
michael@0 | 157 | open(os.path.join(self.tempdir, "test.dmp"), "w").write("foo") |
michael@0 | 158 | self.stdouts.append(["this is some output"]) |
michael@0 | 159 | |
michael@0 | 160 | def make_zipfile(): |
michael@0 | 161 | data = StringIO.StringIO() |
michael@0 | 162 | z = zipfile.ZipFile(data, 'w') |
michael@0 | 163 | z.writestr("symbols.txt", "abc/xyz") |
michael@0 | 164 | z.close() |
michael@0 | 165 | return data.getvalue() |
michael@0 | 166 | def get_symbols(req): |
michael@0 | 167 | headers = {} |
michael@0 | 168 | return (200, headers, make_zipfile()) |
michael@0 | 169 | httpd = mozhttpd.MozHttpd(port=0, |
michael@0 | 170 | urlhandlers=[{'method':'GET', 'path':'/symbols', 'function':get_symbols}]) |
michael@0 | 171 | httpd.start() |
michael@0 | 172 | symbol_url = urlparse.urlunsplit(('http', '%s:%d' % httpd.httpd.server_address, |
michael@0 | 173 | '/symbols','','')) |
michael@0 | 174 | self.assert_(mozcrash.check_for_crashes(self.tempdir, |
michael@0 | 175 | symbol_url, |
michael@0 | 176 | stackwalk_binary=self.stackwalk, |
michael@0 | 177 | quiet=True)) |
michael@0 | 178 | |
michael@0 | 179 | class TestJavaException(unittest.TestCase): |
michael@0 | 180 | def setUp(self): |
michael@0 | 181 | self.test_log = ["01-30 20:15:41.937 E/GeckoAppShell( 1703): >>> REPORTING UNCAUGHT EXCEPTION FROM THREAD 9 (\"GeckoBackgroundThread\")", |
michael@0 | 182 | "01-30 20:15:41.937 E/GeckoAppShell( 1703): java.lang.NullPointerException", |
michael@0 | 183 | "01-30 20:15:41.937 E/GeckoAppShell( 1703): at org.mozilla.gecko.GeckoApp$21.run(GeckoApp.java:1833)", |
michael@0 | 184 | "01-30 20:15:41.937 E/GeckoAppShell( 1703): at android.os.Handler.handleCallback(Handler.java:587)"] |
michael@0 | 185 | |
michael@0 | 186 | def test_uncaught_exception(self): |
michael@0 | 187 | """ |
michael@0 | 188 | Test for an exception which should be caught |
michael@0 | 189 | """ |
michael@0 | 190 | self.assert_(mozcrash.check_for_java_exception(self.test_log, quiet=True)) |
michael@0 | 191 | |
michael@0 | 192 | def test_fatal_exception(self): |
michael@0 | 193 | """ |
michael@0 | 194 | Test for an exception which should be caught |
michael@0 | 195 | """ |
michael@0 | 196 | fatal_log = list(self.test_log) |
michael@0 | 197 | fatal_log[0] = "01-30 20:15:41.937 E/GeckoAppShell( 1703): >>> FATAL EXCEPTION FROM THREAD 9 (\"GeckoBackgroundThread\")" |
michael@0 | 198 | self.assert_(mozcrash.check_for_java_exception(fatal_log, quiet=True)) |
michael@0 | 199 | |
michael@0 | 200 | def test_truncated_exception(self): |
michael@0 | 201 | """ |
michael@0 | 202 | Test for an exception which should be caught which |
michael@0 | 203 | was truncated |
michael@0 | 204 | """ |
michael@0 | 205 | truncated_log = list(self.test_log) |
michael@0 | 206 | truncated_log[0], truncated_log[1] = truncated_log[1], truncated_log[0] |
michael@0 | 207 | self.assert_(mozcrash.check_for_java_exception(truncated_log, quiet=True)) |
michael@0 | 208 | |
michael@0 | 209 | def test_unchecked_exception(self): |
michael@0 | 210 | """ |
michael@0 | 211 | Test for an exception which should not be caught |
michael@0 | 212 | """ |
michael@0 | 213 | passable_log = list(self.test_log) |
michael@0 | 214 | passable_log[0] = "01-30 20:15:41.937 E/GeckoAppShell( 1703): >>> NOT-SO-BAD EXCEPTION FROM THREAD 9 (\"GeckoBackgroundThread\")" |
michael@0 | 215 | self.assert_(not mozcrash.check_for_java_exception(passable_log, quiet=True)) |
michael@0 | 216 | |
michael@0 | 217 | if __name__ == '__main__': |
michael@0 | 218 | unittest.main() |