blob: 62dd0d9ba0635d4fb69640112962576ae8c3ba30 [file] [log] [blame]
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -04001# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Mike Frysingereb753bf2013-11-22 16:05:35 -05005"""Unittests for upload_symbols.py"""
6
Mike Frysinger7f9be142014-01-15 02:16:42 -05007from __future__ import print_function
8
Mike Frysinger0a2fd922014-09-12 20:23:42 -07009import BaseHTTPServer
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040010import ctypes
Mike Frysinger06da5202014-09-26 17:30:33 -050011import errno
Mike Frysingerea838d12014-12-08 11:55:32 -050012import mock
Mike Frysinger0c0efa22014-02-09 23:32:23 -050013import multiprocessing
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040014import os
Mike Frysinger0a2fd922014-09-12 20:23:42 -070015import signal
Mike Frysinger06da5202014-09-26 17:30:33 -050016import socket
Mike Frysinger0a2fd922014-09-12 20:23:42 -070017import SocketServer
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040018import sys
Aviv Keshetd1f04632014-05-09 11:33:46 -070019import time
Mike Frysinger094a2172013-08-14 12:54:35 -040020import urllib2
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040021
Mike Frysinger079863c2014-10-09 23:16:46 -040022# We specifically set up a local server to connect to, so make sure we
23# delete any proxy settings that might screw that up. We also need to
24# do it here because modules that are imported below will implicitly
25# initialize with this proxy setting rather than dynamically pull it
26# on the fly :(.
27os.environ.pop('http_proxy', None)
28
Mike Frysinger02e92402013-11-22 16:22:02 -050029from chromite.lib import cros_build_lib
Ralph Nathan446aee92015-03-23 14:44:56 -070030from chromite.lib import cros_logging as logging
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040031from chromite.lib import cros_test_lib
32from chromite.lib import osutils
33from chromite.lib import parallel
34from chromite.lib import parallel_unittest
Mike Frysinger0a2fd922014-09-12 20:23:42 -070035from chromite.lib import remote_access
Mike Frysinger5e30a4b2014-02-12 20:23:04 -050036from chromite.scripts import cros_generate_breakpad_symbols
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040037from chromite.scripts import upload_symbols
38
Mike Frysinger0c0efa22014-02-09 23:32:23 -050039import isolateserver
Mike Frysinger0c0efa22014-02-09 23:32:23 -050040
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040041
Mike Frysinger0a2fd922014-09-12 20:23:42 -070042class SymbolServerRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
43 """HTTP handler for symbol POSTs"""
44
45 RESP_CODE = None
46 RESP_MSG = None
47
48 def do_POST(self):
49 """Handle a POST request"""
50 # Drain the data from the client. If we don't, we might write the response
51 # and close the socket before the client finishes, so they die with EPIPE.
52 clen = int(self.headers.get('Content-Length', '0'))
53 self.rfile.read(clen)
54
55 self.send_response(self.RESP_CODE, self.RESP_MSG)
56 self.end_headers()
57
58 def log_message(self, *args, **kwargs):
59 """Stub the logger as it writes to stderr"""
60 pass
61
62
63class SymbolServer(SocketServer.ThreadingTCPServer, BaseHTTPServer.HTTPServer):
64 """Simple HTTP server that forks each request"""
65
66
67class UploadSymbolsServerTest(cros_test_lib.MockTempDirTestCase):
68 """Tests for UploadSymbols() and a local HTTP server"""
69
70 SYM_CONTENTS = """MODULE Linux arm 123-456 blkid
71PUBLIC 1471 0 main"""
72
73 def SpawnServer(self, RequestHandler):
74 """Spawn a new http server"""
Mike Frysinger06da5202014-09-26 17:30:33 -050075 while True:
76 try:
77 port = remote_access.GetUnusedPort()
78 address = ('', port)
79 self.httpd = SymbolServer(address, RequestHandler)
80 break
81 except socket.error as e:
82 if e.errno == errno.EADDRINUSE:
83 continue
84 raise
Mike Frysinger0a2fd922014-09-12 20:23:42 -070085 self.server = 'http://localhost:%i' % port
Mike Frysinger0a2fd922014-09-12 20:23:42 -070086 self.httpd_pid = os.fork()
87 if self.httpd_pid == 0:
88 self.httpd.serve_forever(poll_interval=0.1)
89 sys.exit(0)
90
91 def setUp(self):
92 self.httpd_pid = None
93 self.httpd = None
94 self.server = None
95 self.sym_file = os.path.join(self.tempdir, 'test.sym')
96 osutils.WriteFile(self.sym_file, self.SYM_CONTENTS)
97
98 def tearDown(self):
99 # Only kill the server if we forked one.
100 if self.httpd_pid:
101 os.kill(self.httpd_pid, signal.SIGUSR1)
102
103 def testSuccess(self):
104 """The server returns success for all uploads"""
105 class Handler(SymbolServerRequestHandler):
106 """Always return 200"""
107 RESP_CODE = 200
108
109 self.SpawnServer(Handler)
110 ret = upload_symbols.UploadSymbols('', server=self.server, sleep=0,
111 sym_paths=[self.sym_file] * 10,
112 retry=False)
113 self.assertEqual(ret, 0)
114
115 def testError(self):
116 """The server returns errors for all uploads"""
117 class Handler(SymbolServerRequestHandler):
118 """Always return 500"""
119 RESP_CODE = 500
120 RESP_MSG = 'Internal Server Error'
121
122 self.SpawnServer(Handler)
123 ret = upload_symbols.UploadSymbols('', server=self.server, sleep=0,
124 sym_paths=[self.sym_file] * 10,
125 retry=False)
126 self.assertEqual(ret, 4)
127
128 def testHungServer(self):
129 """The server chokes, but we recover"""
130 class Handler(SymbolServerRequestHandler):
131 """All connections choke forever"""
132 def do_POST(self):
133 while True:
134 time.sleep(1000)
135
136 self.SpawnServer(Handler)
137 with mock.patch.object(upload_symbols, 'GetUploadTimeout') as m:
138 m.return_value = 0.1
139 ret = upload_symbols.UploadSymbols('', server=self.server, sleep=0,
140 sym_paths=[self.sym_file] * 10,
141 retry=False)
142 self.assertEqual(ret, 4)
143
144
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400145class UploadSymbolsTest(cros_test_lib.MockTempDirTestCase):
Mike Frysingerc4ab5782013-10-02 18:14:22 -0400146 """Tests for UploadSymbols()"""
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400147
148 def setUp(self):
149 for d in ('foo', 'bar', 'some/dir/here'):
150 d = os.path.join(self.tempdir, d)
151 osutils.SafeMakedirs(d)
152 for f in ('ignored', 'real.sym', 'no.sym.here'):
153 f = os.path.join(d, f)
154 osutils.Touch(f)
Mike Frysingerd41938e2014-02-10 06:37:55 -0500155 self.sym_paths = [
156 'bar/real.sym',
157 'foo/real.sym',
158 'some/dir/here/real.sym',
159 ]
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400160
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500161 self.upload_mock = self.PatchObject(upload_symbols, 'UploadSymbol')
162 self.PatchObject(cros_generate_breakpad_symbols, 'ReadSymsHeader',
163 return_value=cros_generate_breakpad_symbols.SymbolHeader(
164 os='os', cpu='cpu', id='id', name='name'))
165
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400166 def _testUploadURL(self, official, expected_url):
167 """Helper for checking the url used"""
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500168 self.upload_mock.return_value = 0
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400169 with parallel_unittest.ParallelMock():
Mike Frysinger02e92402013-11-22 16:22:02 -0500170 ret = upload_symbols.UploadSymbols('', official=official, retry=False,
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400171 breakpad_dir=self.tempdir, sleep=0)
172 self.assertEqual(ret, 0)
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500173 self.assertEqual(self.upload_mock.call_count, 3)
174 for call_args in self.upload_mock.call_args_list:
Fang Dengba680462015-08-16 20:34:11 -0700175 url, sym_element = call_args[0]
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400176 self.assertEqual(url, expected_url)
Fang Dengba680462015-08-16 20:34:11 -0700177 self.assertTrue(sym_element.symbol_item.sym_file.endswith('.sym'))
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400178
179 def testOfficialUploadURL(self):
180 """Verify we upload to the real crash server for official builds"""
181 self._testUploadURL(True, upload_symbols.OFFICIAL_UPLOAD_URL)
182
183 def testUnofficialUploadURL(self):
184 """Verify we upload to the staging crash server for unofficial builds"""
185 self._testUploadURL(False, upload_symbols.STAGING_UPLOAD_URL)
186
187 def testUploadSymbolFailureSimple(self):
188 """Verify that when UploadSymbol fails, the error count is passed up"""
189 def UploadSymbol(*_args, **kwargs):
190 kwargs['num_errors'].value = 4
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500191 self.upload_mock.side_effect = UploadSymbol
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400192 with parallel_unittest.ParallelMock():
Mike Frysinger02e92402013-11-22 16:22:02 -0500193 ret = upload_symbols.UploadSymbols('', breakpad_dir=self.tempdir, sleep=0,
194 retry=False)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400195 self.assertEquals(ret, 4)
196
197 def testUploadCount(self):
198 """Verify we can limit the number of uploaded symbols"""
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500199 self.upload_mock.return_value = 0
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400200 for c in xrange(3):
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500201 self.upload_mock.reset_mock()
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400202 with parallel_unittest.ParallelMock():
203 ret = upload_symbols.UploadSymbols('', breakpad_dir=self.tempdir,
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500204 sleep=0, upload_limit=c)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400205 self.assertEquals(ret, 0)
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500206 self.assertEqual(self.upload_mock.call_count, c)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400207
Mike Frysinger7f9be142014-01-15 02:16:42 -0500208 def testFailedFileList(self):
209 """Verify the failed file list is populated with the right content"""
210 def UploadSymbol(*args, **kwargs):
Fang Dengba680462015-08-16 20:34:11 -0700211 kwargs['failed_queue'].put(args[1].symbol_item.sym_file)
Mike Frysinger7f9be142014-01-15 02:16:42 -0500212 kwargs['num_errors'].value = 4
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500213 self.upload_mock.side_effect = UploadSymbol
Mike Frysinger7f9be142014-01-15 02:16:42 -0500214 with parallel_unittest.ParallelMock():
215 failed_list = os.path.join(self.tempdir, 'list')
216 ret = upload_symbols.UploadSymbols('', breakpad_dir=self.tempdir, sleep=0,
217 retry=False, failed_list=failed_list)
218 self.assertEquals(ret, 4)
219
220 # Need to sort the output as parallel/fs discovery can be unordered.
221 got_list = sorted(osutils.ReadFile(failed_list).splitlines())
Mike Frysingerd41938e2014-02-10 06:37:55 -0500222 self.assertEquals(self.sym_paths, got_list)
223
224 def _testUpload(self, inputs, sym_paths=None):
225 """Helper for testing uploading of specific paths"""
226 if sym_paths is None:
227 sym_paths = inputs
228
229 self.upload_mock.return_value = 0
230 with parallel_unittest.ParallelMock():
231 ret = upload_symbols.UploadSymbols(sym_paths=inputs, sleep=0,
232 retry=False)
233 self.assertEquals(ret, 0)
234 self.assertEquals(self.upload_mock.call_count, len(sym_paths))
235
236 # Since upload order is arbitrary, we have to do a manual scan for each
237 # path ourselves against the uploaded file list.
Fang Dengba680462015-08-16 20:34:11 -0700238 found_syms = [x[0][1].symbol_item.sym_file
239 for x in self.upload_mock.call_args_list]
Mike Frysingerd41938e2014-02-10 06:37:55 -0500240 for found_sym in found_syms:
241 for path in sym_paths:
242 if found_sym.endswith(path):
243 break
244 else:
245 raise AssertionError('Could not locate %s in %r' % (path, found_syms))
246
247 def testUploadFiles(self):
248 """Test uploading specific symbol files"""
249 sym_paths = (
250 os.path.join(self.tempdir, 'bar', 'real.sym'),
251 os.path.join(self.tempdir, 'foo', 'real.sym'),
252 )
253 self._testUpload(sym_paths)
254
255 def testUploadDirectory(self):
256 """Test uploading directory of symbol files"""
257 self._testUpload([self.tempdir], sym_paths=self.sym_paths)
258
259 def testUploadLocalTarball(self):
260 """Test uploading symbols contains in a local tarball"""
261 tarball = os.path.join(self.tempdir, 'syms.tar.gz')
262 cros_build_lib.CreateTarball(
263 'syms.tar.gz', self.tempdir, compression=cros_build_lib.COMP_GZIP,
264 inputs=('foo', 'bar', 'some'))
265 self._testUpload([tarball], sym_paths=self.sym_paths)
266
267 def testUploadRemoteTarball(self):
268 """Test uploading symbols contains in a remote tarball"""
269 # TODO: Need to figure out how to mock out lib.cache.TarballCache.
Mike Frysinger7f9be142014-01-15 02:16:42 -0500270
Mike Frysinger58312e92014-03-18 04:18:36 -0400271 def testDedupeNotifyFailure(self):
272 """Test that a dedupe server failure midway doesn't wedge things"""
273 api_mock = mock.MagicMock()
274
275 def _Contains(items):
276 """Do not dedupe anything"""
277 return items
278 api_mock.contains.side_effect = _Contains
279
280 # Use a list so the closure below can modify the value.
281 item_count = [0]
282 # Pick a number big enough to trigger a hang normally, but not so
283 # big it adds a lot of overhead.
284 item_limit = 50
285 def _Push(*_args):
286 """Die in the middle of the push list"""
287 item_count[0] += 1
288 if item_count[0] > (item_limit / 10):
289 raise ValueError('time to die')
290 api_mock.push.side_effect = _Push
291
292 self.PatchObject(isolateserver, 'get_storage_api', return_value=api_mock)
293
294 def _Uploader(*args, **kwargs):
295 """Pass the uploaded symbol to the deduper"""
296 sym_item = args[1]
297 passed_queue = kwargs['passed_queue']
298 passed_queue.put(sym_item)
299 self.upload_mock.side_effect = _Uploader
300
301 self.upload_mock.return_value = 0
302 with parallel_unittest.ParallelMock():
303 ret = upload_symbols.UploadSymbols(
304 '', sym_paths=[self.tempdir] * item_limit, sleep=0,
305 dedupe_namespace='inva!id name$pace')
306 self.assertEqual(ret, 0)
307 # This test normally passes by not hanging.
308
Aviv Keshetd1f04632014-05-09 11:33:46 -0700309 def testSlowDedupeSystem(self):
310 """Verify a slow-to-join process doesn't break things when dedupe is off"""
311 # The sleep value here is inherently a little racy, but seems to be good
312 # enough to trigger the bug on a semi-regular basis on developer systems.
313 self.PatchObject(upload_symbols, 'SymbolDeduplicatorNotify',
314 side_effect=lambda *args: time.sleep(1))
315 # Test passing means the code didn't throw an exception.
316 upload_symbols.UploadSymbols(sym_paths=[self.tempdir])
317
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400318
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500319class SymbolDeduplicatorNotifyTest(cros_test_lib.MockTestCase):
320 """Tests for SymbolDeduplicatorNotify()"""
321
322 def setUp(self):
323 self.storage_mock = self.PatchObject(isolateserver, 'get_storage_api')
324
325 def testSmoke(self):
326 """Basic run through the system."""
327 q = mock.MagicMock()
328 q.get.side_effect = (upload_symbols.FakeItem(), None,)
329 upload_symbols.SymbolDeduplicatorNotify('name', q)
330
331 def testStorageException(self):
332 """We want to just warn & move on when dedupe server fails"""
Ralph Nathan446aee92015-03-23 14:44:56 -0700333 log_mock = self.PatchObject(logging, 'warning')
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500334 q = mock.MagicMock()
335 q.get.side_effect = (upload_symbols.FakeItem(), None,)
336 self.storage_mock.side_effect = Exception
337 upload_symbols.SymbolDeduplicatorNotify('name', q)
338 self.assertEqual(log_mock.call_count, 1)
339
340
341class SymbolDeduplicatorTest(cros_test_lib.MockTestCase):
342 """Tests for SymbolDeduplicator()"""
343
344 def setUp(self):
345 self.storage_mock = mock.MagicMock()
346 self.header_mock = self.PatchObject(
347 cros_generate_breakpad_symbols, 'ReadSymsHeader',
348 return_value=cros_generate_breakpad_symbols.SymbolHeader(
349 os='os', cpu='cpu', id='id', name='name'))
350
351 def testNoStorageOrPaths(self):
352 """We don't want to talk to the server if there's no storage or files"""
353 upload_symbols.SymbolDeduplicator(None, [])
354 upload_symbols.SymbolDeduplicator(self.storage_mock, [])
355 self.assertEqual(self.storage_mock.call_count, 0)
356 self.assertEqual(self.header_mock.call_count, 0)
357
358 def testStorageException(self):
359 """We want to just warn & move on when dedupe server fails"""
Ralph Nathan446aee92015-03-23 14:44:56 -0700360 log_mock = self.PatchObject(logging, 'warning')
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500361 self.storage_mock.contains.side_effect = Exception('storage error')
362 sym_paths = ['/a', '/bbbbbb', '/cc.c']
363 ret = upload_symbols.SymbolDeduplicator(self.storage_mock, sym_paths)
364 self.assertEqual(log_mock.call_count, 1)
365 self.assertEqual(self.storage_mock.contains.call_count, 1)
366 self.assertEqual(self.header_mock.call_count, len(sym_paths))
367 self.assertEqual(len(ret), len(sym_paths))
368
369
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400370class UploadSymbolTest(cros_test_lib.MockTempDirTestCase):
Mike Frysingerc4ab5782013-10-02 18:14:22 -0400371 """Tests for UploadSymbol()"""
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400372
373 def setUp(self):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400374 self.sym_file = os.path.join(self.tempdir, 'foo.sym')
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500375 self.sym_item = upload_symbols.FakeItem(sym_file=self.sym_file)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400376 self.url = 'http://eatit'
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500377 self.upload_mock = self.PatchObject(upload_symbols, 'SymUpload')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400378
379 def testUploadSymbolNormal(self):
380 """Verify we try to upload on a normal file"""
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400381 osutils.Touch(self.sym_file)
Fang Dengba680462015-08-16 20:34:11 -0700382 sym_element = upload_symbols.SymbolElement(self.sym_item, None)
Don Garrettcea3eea2015-09-29 17:20:46 -0700383 ret = upload_symbols.UploadSymbol(self.url, sym_element, 'TestProduct')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400384 self.assertEqual(ret, 0)
Don Garrettcea3eea2015-09-29 17:20:46 -0700385 self.upload_mock.assert_called_with(self.url, self.sym_item, 'TestProduct')
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500386 self.assertEqual(self.upload_mock.call_count, 1)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400387
388 def testUploadSymbolErrorCountExceeded(self):
389 """Verify that when the error count gets too high, we stop uploading"""
390 errors = ctypes.c_int(10000)
391 # Pass in garbage values so that we crash if num_errors isn't handled.
Fang Dengba680462015-08-16 20:34:11 -0700392 ret = upload_symbols.UploadSymbol(
Don Garrettcea3eea2015-09-29 17:20:46 -0700393 None, upload_symbols.SymbolElement(self.sym_item, None), 'TestProduct',
394 sleep=None, num_errors=errors)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400395 self.assertEqual(ret, 0)
396
Mike Frysingerc4ab5782013-10-02 18:14:22 -0400397 def testUploadRetryErrors(self, side_effect=None):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400398 """Verify that we retry errors (and eventually give up)"""
Mike Frysingerc4ab5782013-10-02 18:14:22 -0400399 if not side_effect:
400 side_effect = urllib2.HTTPError('http://', 400, 'fail', {}, None)
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500401 self.upload_mock.side_effect = side_effect
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400402 errors = ctypes.c_int()
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500403 item = upload_symbols.FakeItem(sym_file='/dev/null')
Fang Dengba680462015-08-16 20:34:11 -0700404 element = upload_symbols.SymbolElement(item, None)
Don Garrettcea3eea2015-09-29 17:20:46 -0700405 ret = upload_symbols.UploadSymbol(self.url, element, 'TestProduct',
406 num_errors=errors)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400407 self.assertEqual(ret, 1)
Don Garrettcea3eea2015-09-29 17:20:46 -0700408 self.upload_mock.assert_called_with(self.url, item, 'TestProduct')
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500409 self.assertTrue(self.upload_mock.call_count >= upload_symbols.MAX_RETRIES)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400410
Mike Frysingerc4ab5782013-10-02 18:14:22 -0400411 def testConnectRetryErrors(self):
412 """Verify that we retry errors (and eventually give up) w/connect errors"""
413 side_effect = urllib2.URLError('foo')
414 self.testUploadRetryErrors(side_effect=side_effect)
415
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400416 def testTruncateTooBigFiles(self):
417 """Verify we shrink big files"""
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500418 def SymUpload(_url, sym_item):
419 content = osutils.ReadFile(sym_item.sym_file)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400420 self.assertEqual(content, 'some junk\n')
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500421 self.upload_mock.upload_mock.side_effect = SymUpload
422 content = '\n'.join((
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400423 'STACK CFI 1234',
424 'some junk',
425 'STACK CFI 1234',
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500426 ))
427 osutils.WriteFile(self.sym_file, content)
Fang Dengba680462015-08-16 20:34:11 -0700428 ret = upload_symbols.UploadSymbol(
429 self.url, upload_symbols.SymbolElement(self.sym_item, None),
Don Garrettcea3eea2015-09-29 17:20:46 -0700430 'TestProduct', file_limit=1)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400431 self.assertEqual(ret, 0)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500432 # Make sure the item passed to the upload has a temp file and not the
433 # original -- only the temp one has been stripped down.
434 temp_item = self.upload_mock.call_args[0][1]
435 self.assertNotEqual(temp_item.sym_file, self.sym_item.sym_file)
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500436 self.assertEqual(self.upload_mock.call_count, 1)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400437
438 def testTruncateReallyLargeFiles(self):
439 """Verify we try to shrink really big files"""
Prathmesh Prabhu17f07422015-07-17 11:40:40 -0700440 warn_mock = self.PatchObject(logging, 'PrintBuildbotStepWarnings')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400441 with open(self.sym_file, 'w+b') as f:
442 f.truncate(upload_symbols.CRASH_SERVER_FILE_LIMIT + 100)
443 f.seek(0)
444 f.write('STACK CFI 1234\n\n')
Fang Dengba680462015-08-16 20:34:11 -0700445 ret = upload_symbols.UploadSymbol(
446 self.url,
Don Garrettcea3eea2015-09-29 17:20:46 -0700447 upload_symbols.SymbolElement(self.sym_item, None),
448 'TestProduct')
Mike Frysinger02e92402013-11-22 16:22:02 -0500449 self.assertEqual(ret, 0)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500450 # Make sure the item passed to the upload has a temp file and not the
451 # original -- only the temp one has been truncated.
452 temp_item = self.upload_mock.call_args[0][1]
453 self.assertNotEqual(temp_item.sym_file, self.sym_item.sym_file)
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500454 self.assertEqual(self.upload_mock.call_count, 1)
Mike Frysinger02e92402013-11-22 16:22:02 -0500455 self.assertEqual(warn_mock.call_count, 1)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400456
457
Mike Frysinger094a2172013-08-14 12:54:35 -0400458class SymUploadTest(cros_test_lib.MockTempDirTestCase):
Mike Frysingerc4ab5782013-10-02 18:14:22 -0400459 """Tests for SymUpload()"""
Mike Frysinger094a2172013-08-14 12:54:35 -0400460
461 SYM_URL = 'http://localhost/post/it/here'
462 SYM_CONTENTS = """MODULE Linux arm 123-456 blkid
463PUBLIC 1471 0 main"""
464
465 def setUp(self):
466 self.sym_file = os.path.join(self.tempdir, 'test.sym')
467 osutils.WriteFile(self.sym_file, self.SYM_CONTENTS)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500468 self.sym_item = upload_symbols.SymbolItem(self.sym_file)
Mike Frysinger094a2172013-08-14 12:54:35 -0400469
470 def testPostUpload(self):
471 """Verify HTTP POST has all the fields we need"""
472 m = self.PatchObject(urllib2, 'urlopen', autospec=True)
Don Garrettcea3eea2015-09-29 17:20:46 -0700473 upload_symbols.SymUpload(self.SYM_URL, self.sym_item, 'TestProduct')
Mike Frysinger094a2172013-08-14 12:54:35 -0400474 self.assertEquals(m.call_count, 1)
475 req = m.call_args[0][0]
476 self.assertEquals(req.get_full_url(), self.SYM_URL)
477 data = ''.join([x for x in req.get_data()])
478
479 fields = {
480 'code_file': 'blkid',
481 'debug_file': 'blkid',
482 'debug_identifier': '123456',
483 'os': 'Linux',
484 'cpu': 'arm',
485 }
486 for key, val in fields.iteritems():
487 line = 'Content-Disposition: form-data; name="%s"\r\n' % key
488 self.assertTrue(line in data)
489 line = '%s\r\n' % val
490 self.assertTrue(line in data)
491 line = ('Content-Disposition: form-data; name="symbol_file"; '
492 'filename="test.sym"\r\n')
493 self.assertTrue(line in data)
494 self.assertTrue(self.SYM_CONTENTS in data)
495
Mike Frysinger71046662014-09-12 18:15:15 -0700496 def testTimeout(self):
497 """Verify timeouts scale based on filesize"""
498 m = self.PatchObject(urllib2, 'urlopen', autospec=True)
499 size = self.PatchObject(os.path, 'getsize')
500
501 tests = (
502 # Small files should get rounded up to the minimum timeout.
503 (10, upload_symbols.UPLOAD_MIN_TIMEOUT),
504 # A 50MiB file should take like ~4 minutes.
505 (50 * 1024 * 1024, 257),
506 )
507 for size.return_value, timeout in tests:
Don Garrettcea3eea2015-09-29 17:20:46 -0700508 upload_symbols.SymUpload(self.SYM_URL, self.sym_item, 'TestProduct')
Mike Frysinger71046662014-09-12 18:15:15 -0700509 self.assertEqual(m.call_args[1]['timeout'], timeout)
510
Mike Frysinger094a2172013-08-14 12:54:35 -0400511
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500512class UtilTest(cros_test_lib.TempDirTestCase):
513 """Various tests for utility funcs."""
514
515 def testWriteQueueToFile(self):
516 """Basic test for WriteQueueToFile."""
517 listing = os.path.join(self.tempdir, 'list')
518 exp_list = [
519 'b/c.txt',
520 'foo.log',
521 'there/might/be/giants',
522 ]
523 relpath = '/a'
524
525 q = multiprocessing.Queue()
526 for f in exp_list:
527 q.put(os.path.join(relpath, f))
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500528 q.put(None)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500529 upload_symbols.WriteQueueToFile(listing, q, '/a')
530
531 got_list = osutils.ReadFile(listing).splitlines()
532 self.assertEquals(exp_list, got_list)
533
534
Mike Frysingerea838d12014-12-08 11:55:32 -0500535def main(_argv):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400536 # pylint: disable=W0212
537 # Set timeouts small so that if the unit test hangs, it won't hang for long.
538 parallel._BackgroundTask.STARTUP_TIMEOUT = 5
539 parallel._BackgroundTask.EXIT_TIMEOUT = 5
540
541 # We want to test retry behavior, so make sure we don't sleep.
542 upload_symbols.INITIAL_RETRY_DELAY = 0
543
544 # Run the tests.
Mike Frysingerba167372015-01-21 10:37:03 -0500545 cros_test_lib.main(level='info', module=__name__)