Mike Frysinger | d5fcb3a | 2013-05-30 21:10:50 -0400 | [diff] [blame] | 1 | #!/usr/bin/python |
| 2 | # Copyright (c) 2013 The Chromium OS Authors. All rights reserved. |
| 3 | # Use of this source code is governed by a BSD-style license that can be |
| 4 | # found in the LICENSE file. |
| 5 | |
Mike Frysinger | eb753bf | 2013-11-22 16:05:35 -0500 | [diff] [blame] | 6 | """Unittests for upload_symbols.py""" |
| 7 | |
Mike Frysinger | 7f9be14 | 2014-01-15 02:16:42 -0500 | [diff] [blame] | 8 | from __future__ import print_function |
| 9 | |
Mike Frysinger | d5fcb3a | 2013-05-30 21:10:50 -0400 | [diff] [blame] | 10 | import ctypes |
| 11 | import logging |
| 12 | import os |
| 13 | import sys |
Mike Frysinger | 094a217 | 2013-08-14 12:54:35 -0400 | [diff] [blame] | 14 | import urllib2 |
Mike Frysinger | d5fcb3a | 2013-05-30 21:10:50 -0400 | [diff] [blame] | 15 | |
| 16 | sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)), |
| 17 | '..', '..')) |
Mike Frysinger | 02e9240 | 2013-11-22 16:22:02 -0500 | [diff] [blame] | 18 | from chromite.lib import cros_build_lib |
Mike Frysinger | d5fcb3a | 2013-05-30 21:10:50 -0400 | [diff] [blame] | 19 | from chromite.lib import cros_test_lib |
| 20 | from chromite.lib import osutils |
| 21 | from chromite.lib import parallel |
| 22 | from chromite.lib import parallel_unittest |
Mike Frysinger | 5e30a4b | 2014-02-12 20:23:04 -0500 | [diff] [blame] | 23 | from chromite.scripts import cros_generate_breakpad_symbols |
Mike Frysinger | d5fcb3a | 2013-05-30 21:10:50 -0400 | [diff] [blame] | 24 | from chromite.scripts import upload_symbols |
| 25 | |
Mike Frysinger | d5fcb3a | 2013-05-30 21:10:50 -0400 | [diff] [blame] | 26 | |
| 27 | class UploadSymbolsTest(cros_test_lib.MockTempDirTestCase): |
Mike Frysinger | c4ab578 | 2013-10-02 18:14:22 -0400 | [diff] [blame] | 28 | """Tests for UploadSymbols()""" |
Mike Frysinger | d5fcb3a | 2013-05-30 21:10:50 -0400 | [diff] [blame] | 29 | |
| 30 | def setUp(self): |
| 31 | for d in ('foo', 'bar', 'some/dir/here'): |
| 32 | d = os.path.join(self.tempdir, d) |
| 33 | osutils.SafeMakedirs(d) |
| 34 | for f in ('ignored', 'real.sym', 'no.sym.here'): |
| 35 | f = os.path.join(d, f) |
| 36 | osutils.Touch(f) |
| 37 | |
Mike Frysinger | 5e30a4b | 2014-02-12 20:23:04 -0500 | [diff] [blame] | 38 | self.upload_mock = self.PatchObject(upload_symbols, 'UploadSymbol') |
| 39 | self.PatchObject(cros_generate_breakpad_symbols, 'ReadSymsHeader', |
| 40 | return_value=cros_generate_breakpad_symbols.SymbolHeader( |
| 41 | os='os', cpu='cpu', id='id', name='name')) |
| 42 | |
Mike Frysinger | d5fcb3a | 2013-05-30 21:10:50 -0400 | [diff] [blame] | 43 | def _testUploadURL(self, official, expected_url): |
| 44 | """Helper for checking the url used""" |
Mike Frysinger | 5e30a4b | 2014-02-12 20:23:04 -0500 | [diff] [blame] | 45 | self.upload_mock.return_value = 0 |
Mike Frysinger | d5fcb3a | 2013-05-30 21:10:50 -0400 | [diff] [blame] | 46 | with parallel_unittest.ParallelMock(): |
Mike Frysinger | 02e9240 | 2013-11-22 16:22:02 -0500 | [diff] [blame] | 47 | ret = upload_symbols.UploadSymbols('', official=official, retry=False, |
Mike Frysinger | d5fcb3a | 2013-05-30 21:10:50 -0400 | [diff] [blame] | 48 | breakpad_dir=self.tempdir, sleep=0) |
| 49 | self.assertEqual(ret, 0) |
Mike Frysinger | 5e30a4b | 2014-02-12 20:23:04 -0500 | [diff] [blame] | 50 | self.assertEqual(self.upload_mock.call_count, 3) |
| 51 | for call_args in self.upload_mock.call_args_list: |
Mike Frysinger | d5fcb3a | 2013-05-30 21:10:50 -0400 | [diff] [blame] | 52 | sym_file, url = call_args[0] |
| 53 | self.assertEqual(url, expected_url) |
| 54 | self.assertTrue(sym_file.endswith('.sym')) |
| 55 | |
| 56 | def testOfficialUploadURL(self): |
| 57 | """Verify we upload to the real crash server for official builds""" |
| 58 | self._testUploadURL(True, upload_symbols.OFFICIAL_UPLOAD_URL) |
| 59 | |
| 60 | def testUnofficialUploadURL(self): |
| 61 | """Verify we upload to the staging crash server for unofficial builds""" |
| 62 | self._testUploadURL(False, upload_symbols.STAGING_UPLOAD_URL) |
| 63 | |
| 64 | def testUploadSymbolFailureSimple(self): |
| 65 | """Verify that when UploadSymbol fails, the error count is passed up""" |
| 66 | def UploadSymbol(*_args, **kwargs): |
| 67 | kwargs['num_errors'].value = 4 |
Mike Frysinger | 5e30a4b | 2014-02-12 20:23:04 -0500 | [diff] [blame] | 68 | self.upload_mock.side_effect = UploadSymbol |
Mike Frysinger | d5fcb3a | 2013-05-30 21:10:50 -0400 | [diff] [blame] | 69 | with parallel_unittest.ParallelMock(): |
Mike Frysinger | 02e9240 | 2013-11-22 16:22:02 -0500 | [diff] [blame] | 70 | ret = upload_symbols.UploadSymbols('', breakpad_dir=self.tempdir, sleep=0, |
| 71 | retry=False) |
Mike Frysinger | d5fcb3a | 2013-05-30 21:10:50 -0400 | [diff] [blame] | 72 | self.assertEquals(ret, 4) |
| 73 | |
| 74 | def testUploadCount(self): |
| 75 | """Verify we can limit the number of uploaded symbols""" |
Mike Frysinger | 5e30a4b | 2014-02-12 20:23:04 -0500 | [diff] [blame] | 76 | self.upload_mock.return_value = 0 |
Mike Frysinger | d5fcb3a | 2013-05-30 21:10:50 -0400 | [diff] [blame] | 77 | for c in xrange(3): |
Mike Frysinger | 5e30a4b | 2014-02-12 20:23:04 -0500 | [diff] [blame] | 78 | self.upload_mock.reset_mock() |
Mike Frysinger | d5fcb3a | 2013-05-30 21:10:50 -0400 | [diff] [blame] | 79 | with parallel_unittest.ParallelMock(): |
| 80 | ret = upload_symbols.UploadSymbols('', breakpad_dir=self.tempdir, |
Mike Frysinger | 8ec8c50 | 2014-02-10 00:19:13 -0500 | [diff] [blame] | 81 | sleep=0, upload_limit=c) |
Mike Frysinger | d5fcb3a | 2013-05-30 21:10:50 -0400 | [diff] [blame] | 82 | self.assertEquals(ret, 0) |
Mike Frysinger | 5e30a4b | 2014-02-12 20:23:04 -0500 | [diff] [blame] | 83 | self.assertEqual(self.upload_mock.call_count, c) |
Mike Frysinger | d5fcb3a | 2013-05-30 21:10:50 -0400 | [diff] [blame] | 84 | |
Mike Frysinger | 7f9be14 | 2014-01-15 02:16:42 -0500 | [diff] [blame] | 85 | def testFailedFileList(self): |
| 86 | """Verify the failed file list is populated with the right content""" |
| 87 | def UploadSymbol(*args, **kwargs): |
| 88 | kwargs['failed_queue'].put(args[0]) |
| 89 | kwargs['num_errors'].value = 4 |
Mike Frysinger | 5e30a4b | 2014-02-12 20:23:04 -0500 | [diff] [blame] | 90 | self.upload_mock.side_effect = UploadSymbol |
Mike Frysinger | 7f9be14 | 2014-01-15 02:16:42 -0500 | [diff] [blame] | 91 | with parallel_unittest.ParallelMock(): |
| 92 | failed_list = os.path.join(self.tempdir, 'list') |
| 93 | ret = upload_symbols.UploadSymbols('', breakpad_dir=self.tempdir, sleep=0, |
| 94 | retry=False, failed_list=failed_list) |
| 95 | self.assertEquals(ret, 4) |
| 96 | |
| 97 | # Need to sort the output as parallel/fs discovery can be unordered. |
| 98 | got_list = sorted(osutils.ReadFile(failed_list).splitlines()) |
| 99 | exp_list = [ |
| 100 | 'bar/real.sym', |
| 101 | 'foo/real.sym', |
| 102 | 'some/dir/here/real.sym', |
| 103 | ] |
| 104 | self.assertEquals(exp_list, got_list) |
| 105 | |
Mike Frysinger | d5fcb3a | 2013-05-30 21:10:50 -0400 | [diff] [blame] | 106 | |
| 107 | class UploadSymbolTest(cros_test_lib.MockTempDirTestCase): |
Mike Frysinger | c4ab578 | 2013-10-02 18:14:22 -0400 | [diff] [blame] | 108 | """Tests for UploadSymbol()""" |
Mike Frysinger | d5fcb3a | 2013-05-30 21:10:50 -0400 | [diff] [blame] | 109 | |
| 110 | def setUp(self): |
Mike Frysinger | d5fcb3a | 2013-05-30 21:10:50 -0400 | [diff] [blame] | 111 | self.sym_file = os.path.join(self.tempdir, 'foo.sym') |
| 112 | self.url = 'http://eatit' |
Mike Frysinger | 5e30a4b | 2014-02-12 20:23:04 -0500 | [diff] [blame] | 113 | self.upload_mock = self.PatchObject(upload_symbols, 'SymUpload') |
Mike Frysinger | d5fcb3a | 2013-05-30 21:10:50 -0400 | [diff] [blame] | 114 | |
| 115 | def testUploadSymbolNormal(self): |
| 116 | """Verify we try to upload on a normal file""" |
Mike Frysinger | d5fcb3a | 2013-05-30 21:10:50 -0400 | [diff] [blame] | 117 | osutils.Touch(self.sym_file) |
| 118 | ret = upload_symbols.UploadSymbol(self.sym_file, self.url) |
| 119 | self.assertEqual(ret, 0) |
Mike Frysinger | 5e30a4b | 2014-02-12 20:23:04 -0500 | [diff] [blame] | 120 | self.upload_mock.assert_called_with(self.sym_file, self.url) |
| 121 | self.assertEqual(self.upload_mock.call_count, 1) |
Mike Frysinger | d5fcb3a | 2013-05-30 21:10:50 -0400 | [diff] [blame] | 122 | |
| 123 | def testUploadSymbolErrorCountExceeded(self): |
| 124 | """Verify that when the error count gets too high, we stop uploading""" |
| 125 | errors = ctypes.c_int(10000) |
| 126 | # Pass in garbage values so that we crash if num_errors isn't handled. |
| 127 | ret = upload_symbols.UploadSymbol(None, None, sleep=None, num_errors=errors) |
| 128 | self.assertEqual(ret, 0) |
| 129 | |
Mike Frysinger | c4ab578 | 2013-10-02 18:14:22 -0400 | [diff] [blame] | 130 | def testUploadRetryErrors(self, side_effect=None): |
Mike Frysinger | d5fcb3a | 2013-05-30 21:10:50 -0400 | [diff] [blame] | 131 | """Verify that we retry errors (and eventually give up)""" |
Mike Frysinger | c4ab578 | 2013-10-02 18:14:22 -0400 | [diff] [blame] | 132 | if not side_effect: |
| 133 | side_effect = urllib2.HTTPError('http://', 400, 'fail', {}, None) |
Mike Frysinger | 5e30a4b | 2014-02-12 20:23:04 -0500 | [diff] [blame] | 134 | self.upload_mock.side_effect = side_effect |
Mike Frysinger | d5fcb3a | 2013-05-30 21:10:50 -0400 | [diff] [blame] | 135 | errors = ctypes.c_int() |
| 136 | ret = upload_symbols.UploadSymbol('/dev/null', self.url, num_errors=errors) |
| 137 | self.assertEqual(ret, 1) |
Mike Frysinger | 5e30a4b | 2014-02-12 20:23:04 -0500 | [diff] [blame] | 138 | self.upload_mock.assert_called_with('/dev/null', self.url) |
| 139 | self.assertTrue(self.upload_mock.call_count >= upload_symbols.MAX_RETRIES) |
Mike Frysinger | d5fcb3a | 2013-05-30 21:10:50 -0400 | [diff] [blame] | 140 | |
Mike Frysinger | c4ab578 | 2013-10-02 18:14:22 -0400 | [diff] [blame] | 141 | def testConnectRetryErrors(self): |
| 142 | """Verify that we retry errors (and eventually give up) w/connect errors""" |
| 143 | side_effect = urllib2.URLError('foo') |
| 144 | self.testUploadRetryErrors(side_effect=side_effect) |
| 145 | |
Mike Frysinger | d5fcb3a | 2013-05-30 21:10:50 -0400 | [diff] [blame] | 146 | def testTruncateTooBigFiles(self): |
| 147 | """Verify we shrink big files""" |
| 148 | def SymUpload(sym_file, _url): |
| 149 | content = osutils.ReadFile(sym_file) |
| 150 | self.assertEqual(content, 'some junk\n') |
Mike Frysinger | 5e30a4b | 2014-02-12 20:23:04 -0500 | [diff] [blame] | 151 | self.upload_mock.upload_mock.side_effect = SymUpload |
| 152 | content = '\n'.join(( |
Mike Frysinger | d5fcb3a | 2013-05-30 21:10:50 -0400 | [diff] [blame] | 153 | 'STACK CFI 1234', |
| 154 | 'some junk', |
| 155 | 'STACK CFI 1234', |
Mike Frysinger | 5e30a4b | 2014-02-12 20:23:04 -0500 | [diff] [blame] | 156 | )) |
| 157 | osutils.WriteFile(self.sym_file, content) |
Mike Frysinger | d5fcb3a | 2013-05-30 21:10:50 -0400 | [diff] [blame] | 158 | ret = upload_symbols.UploadSymbol(self.sym_file, self.url, file_limit=1) |
| 159 | self.assertEqual(ret, 0) |
Mike Frysinger | 5e30a4b | 2014-02-12 20:23:04 -0500 | [diff] [blame] | 160 | self.assertNotEqual(self.upload_mock.call_args[0][1], self.sym_file) |
| 161 | self.assertEqual(self.upload_mock.call_count, 1) |
Mike Frysinger | d5fcb3a | 2013-05-30 21:10:50 -0400 | [diff] [blame] | 162 | |
| 163 | def testTruncateReallyLargeFiles(self): |
| 164 | """Verify we try to shrink really big files""" |
Mike Frysinger | 02e9240 | 2013-11-22 16:22:02 -0500 | [diff] [blame] | 165 | warn_mock = self.PatchObject(cros_build_lib, 'PrintBuildbotStepWarnings') |
Mike Frysinger | d5fcb3a | 2013-05-30 21:10:50 -0400 | [diff] [blame] | 166 | with open(self.sym_file, 'w+b') as f: |
| 167 | f.truncate(upload_symbols.CRASH_SERVER_FILE_LIMIT + 100) |
| 168 | f.seek(0) |
| 169 | f.write('STACK CFI 1234\n\n') |
| 170 | ret = upload_symbols.UploadSymbol(self.sym_file, self.url) |
Mike Frysinger | 02e9240 | 2013-11-22 16:22:02 -0500 | [diff] [blame] | 171 | self.assertEqual(ret, 0) |
Mike Frysinger | 5e30a4b | 2014-02-12 20:23:04 -0500 | [diff] [blame] | 172 | self.assertNotEqual(self.upload_mock.call_args[0][1], self.sym_file) |
| 173 | self.assertEqual(self.upload_mock.call_count, 1) |
Mike Frysinger | 02e9240 | 2013-11-22 16:22:02 -0500 | [diff] [blame] | 174 | self.assertEqual(warn_mock.call_count, 1) |
Mike Frysinger | d5fcb3a | 2013-05-30 21:10:50 -0400 | [diff] [blame] | 175 | |
| 176 | |
Mike Frysinger | 094a217 | 2013-08-14 12:54:35 -0400 | [diff] [blame] | 177 | class SymUploadTest(cros_test_lib.MockTempDirTestCase): |
Mike Frysinger | c4ab578 | 2013-10-02 18:14:22 -0400 | [diff] [blame] | 178 | """Tests for SymUpload()""" |
Mike Frysinger | 094a217 | 2013-08-14 12:54:35 -0400 | [diff] [blame] | 179 | |
| 180 | SYM_URL = 'http://localhost/post/it/here' |
| 181 | SYM_CONTENTS = """MODULE Linux arm 123-456 blkid |
| 182 | PUBLIC 1471 0 main""" |
| 183 | |
| 184 | def setUp(self): |
| 185 | self.sym_file = os.path.join(self.tempdir, 'test.sym') |
| 186 | osutils.WriteFile(self.sym_file, self.SYM_CONTENTS) |
| 187 | |
| 188 | def testPostUpload(self): |
| 189 | """Verify HTTP POST has all the fields we need""" |
| 190 | m = self.PatchObject(urllib2, 'urlopen', autospec=True) |
| 191 | upload_symbols.SymUpload(self.sym_file, self.SYM_URL) |
| 192 | self.assertEquals(m.call_count, 1) |
| 193 | req = m.call_args[0][0] |
| 194 | self.assertEquals(req.get_full_url(), self.SYM_URL) |
| 195 | data = ''.join([x for x in req.get_data()]) |
| 196 | |
| 197 | fields = { |
| 198 | 'code_file': 'blkid', |
| 199 | 'debug_file': 'blkid', |
| 200 | 'debug_identifier': '123456', |
| 201 | 'os': 'Linux', |
| 202 | 'cpu': 'arm', |
| 203 | } |
| 204 | for key, val in fields.iteritems(): |
| 205 | line = 'Content-Disposition: form-data; name="%s"\r\n' % key |
| 206 | self.assertTrue(line in data) |
| 207 | line = '%s\r\n' % val |
| 208 | self.assertTrue(line in data) |
| 209 | line = ('Content-Disposition: form-data; name="symbol_file"; ' |
| 210 | 'filename="test.sym"\r\n') |
| 211 | self.assertTrue(line in data) |
| 212 | self.assertTrue(self.SYM_CONTENTS in data) |
| 213 | |
| 214 | |
Mike Frysinger | d5fcb3a | 2013-05-30 21:10:50 -0400 | [diff] [blame] | 215 | if __name__ == '__main__': |
| 216 | # pylint: disable=W0212 |
| 217 | # Set timeouts small so that if the unit test hangs, it won't hang for long. |
| 218 | parallel._BackgroundTask.STARTUP_TIMEOUT = 5 |
| 219 | parallel._BackgroundTask.EXIT_TIMEOUT = 5 |
| 220 | |
| 221 | # We want to test retry behavior, so make sure we don't sleep. |
| 222 | upload_symbols.INITIAL_RETRY_DELAY = 0 |
| 223 | |
| 224 | # Run the tests. |
| 225 | cros_test_lib.main(level=logging.INFO) |