blob: b9d7c548558aa88890bdf4c06d31399e636f20e2 [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
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040030from chromite.lib import cros_test_lib
31from chromite.lib import osutils
32from chromite.lib import parallel
33from chromite.lib import parallel_unittest
Mike Frysinger0a2fd922014-09-12 20:23:42 -070034from chromite.lib import remote_access
Mike Frysinger5e30a4b2014-02-12 20:23:04 -050035from chromite.scripts import cros_generate_breakpad_symbols
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040036from chromite.scripts import upload_symbols
37
Mike Frysinger0c0efa22014-02-09 23:32:23 -050038import isolateserver
Mike Frysinger0c0efa22014-02-09 23:32:23 -050039
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040040
Mike Frysinger0a2fd922014-09-12 20:23:42 -070041class SymbolServerRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
42 """HTTP handler for symbol POSTs"""
43
44 RESP_CODE = None
45 RESP_MSG = None
46
47 def do_POST(self):
48 """Handle a POST request"""
49 # Drain the data from the client. If we don't, we might write the response
50 # and close the socket before the client finishes, so they die with EPIPE.
51 clen = int(self.headers.get('Content-Length', '0'))
52 self.rfile.read(clen)
53
54 self.send_response(self.RESP_CODE, self.RESP_MSG)
55 self.end_headers()
56
57 def log_message(self, *args, **kwargs):
58 """Stub the logger as it writes to stderr"""
59 pass
60
61
62class SymbolServer(SocketServer.ThreadingTCPServer, BaseHTTPServer.HTTPServer):
63 """Simple HTTP server that forks each request"""
64
65
66class UploadSymbolsServerTest(cros_test_lib.MockTempDirTestCase):
67 """Tests for UploadSymbols() and a local HTTP server"""
68
69 SYM_CONTENTS = """MODULE Linux arm 123-456 blkid
70PUBLIC 1471 0 main"""
71
72 def SpawnServer(self, RequestHandler):
73 """Spawn a new http server"""
Mike Frysinger06da5202014-09-26 17:30:33 -050074 while True:
75 try:
76 port = remote_access.GetUnusedPort()
77 address = ('', port)
78 self.httpd = SymbolServer(address, RequestHandler)
79 break
80 except socket.error as e:
81 if e.errno == errno.EADDRINUSE:
82 continue
83 raise
Mike Frysinger0a2fd922014-09-12 20:23:42 -070084 self.server = 'http://localhost:%i' % port
Mike Frysinger0a2fd922014-09-12 20:23:42 -070085 self.httpd_pid = os.fork()
86 if self.httpd_pid == 0:
87 self.httpd.serve_forever(poll_interval=0.1)
88 sys.exit(0)
89
90 def setUp(self):
91 self.httpd_pid = None
92 self.httpd = None
93 self.server = None
94 self.sym_file = os.path.join(self.tempdir, 'test.sym')
95 osutils.WriteFile(self.sym_file, self.SYM_CONTENTS)
96
97 def tearDown(self):
98 # Only kill the server if we forked one.
99 if self.httpd_pid:
100 os.kill(self.httpd_pid, signal.SIGUSR1)
101
102 def testSuccess(self):
103 """The server returns success for all uploads"""
104 class Handler(SymbolServerRequestHandler):
105 """Always return 200"""
106 RESP_CODE = 200
107
108 self.SpawnServer(Handler)
109 ret = upload_symbols.UploadSymbols('', server=self.server, sleep=0,
110 sym_paths=[self.sym_file] * 10,
111 retry=False)
112 self.assertEqual(ret, 0)
113
114 def testError(self):
115 """The server returns errors for all uploads"""
116 class Handler(SymbolServerRequestHandler):
117 """Always return 500"""
118 RESP_CODE = 500
119 RESP_MSG = 'Internal Server Error'
120
121 self.SpawnServer(Handler)
122 ret = upload_symbols.UploadSymbols('', server=self.server, sleep=0,
123 sym_paths=[self.sym_file] * 10,
124 retry=False)
125 self.assertEqual(ret, 4)
126
127 def testHungServer(self):
128 """The server chokes, but we recover"""
129 class Handler(SymbolServerRequestHandler):
130 """All connections choke forever"""
131 def do_POST(self):
132 while True:
133 time.sleep(1000)
134
135 self.SpawnServer(Handler)
136 with mock.patch.object(upload_symbols, 'GetUploadTimeout') as m:
137 m.return_value = 0.1
138 ret = upload_symbols.UploadSymbols('', server=self.server, sleep=0,
139 sym_paths=[self.sym_file] * 10,
140 retry=False)
141 self.assertEqual(ret, 4)
142
143
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400144class UploadSymbolsTest(cros_test_lib.MockTempDirTestCase):
Mike Frysingerc4ab5782013-10-02 18:14:22 -0400145 """Tests for UploadSymbols()"""
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400146
147 def setUp(self):
148 for d in ('foo', 'bar', 'some/dir/here'):
149 d = os.path.join(self.tempdir, d)
150 osutils.SafeMakedirs(d)
151 for f in ('ignored', 'real.sym', 'no.sym.here'):
152 f = os.path.join(d, f)
153 osutils.Touch(f)
Mike Frysingerd41938e2014-02-10 06:37:55 -0500154 self.sym_paths = [
155 'bar/real.sym',
156 'foo/real.sym',
157 'some/dir/here/real.sym',
158 ]
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400159
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500160 self.upload_mock = self.PatchObject(upload_symbols, 'UploadSymbol')
161 self.PatchObject(cros_generate_breakpad_symbols, 'ReadSymsHeader',
162 return_value=cros_generate_breakpad_symbols.SymbolHeader(
163 os='os', cpu='cpu', id='id', name='name'))
164
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400165 def _testUploadURL(self, official, expected_url):
166 """Helper for checking the url used"""
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500167 self.upload_mock.return_value = 0
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400168 with parallel_unittest.ParallelMock():
Mike Frysinger02e92402013-11-22 16:22:02 -0500169 ret = upload_symbols.UploadSymbols('', official=official, retry=False,
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400170 breakpad_dir=self.tempdir, sleep=0)
171 self.assertEqual(ret, 0)
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500172 self.assertEqual(self.upload_mock.call_count, 3)
173 for call_args in self.upload_mock.call_args_list:
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500174 url, sym_item = call_args[0]
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400175 self.assertEqual(url, expected_url)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500176 self.assertTrue(sym_item.sym_file.endswith('.sym'))
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400177
178 def testOfficialUploadURL(self):
179 """Verify we upload to the real crash server for official builds"""
180 self._testUploadURL(True, upload_symbols.OFFICIAL_UPLOAD_URL)
181
182 def testUnofficialUploadURL(self):
183 """Verify we upload to the staging crash server for unofficial builds"""
184 self._testUploadURL(False, upload_symbols.STAGING_UPLOAD_URL)
185
186 def testUploadSymbolFailureSimple(self):
187 """Verify that when UploadSymbol fails, the error count is passed up"""
188 def UploadSymbol(*_args, **kwargs):
189 kwargs['num_errors'].value = 4
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500190 self.upload_mock.side_effect = UploadSymbol
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400191 with parallel_unittest.ParallelMock():
Mike Frysinger02e92402013-11-22 16:22:02 -0500192 ret = upload_symbols.UploadSymbols('', breakpad_dir=self.tempdir, sleep=0,
193 retry=False)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400194 self.assertEquals(ret, 4)
195
196 def testUploadCount(self):
197 """Verify we can limit the number of uploaded symbols"""
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500198 self.upload_mock.return_value = 0
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400199 for c in xrange(3):
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500200 self.upload_mock.reset_mock()
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400201 with parallel_unittest.ParallelMock():
202 ret = upload_symbols.UploadSymbols('', breakpad_dir=self.tempdir,
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500203 sleep=0, upload_limit=c)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400204 self.assertEquals(ret, 0)
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500205 self.assertEqual(self.upload_mock.call_count, c)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400206
Mike Frysinger7f9be142014-01-15 02:16:42 -0500207 def testFailedFileList(self):
208 """Verify the failed file list is populated with the right content"""
209 def UploadSymbol(*args, **kwargs):
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500210 kwargs['failed_queue'].put(args[1].sym_file)
Mike Frysinger7f9be142014-01-15 02:16:42 -0500211 kwargs['num_errors'].value = 4
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500212 self.upload_mock.side_effect = UploadSymbol
Mike Frysinger7f9be142014-01-15 02:16:42 -0500213 with parallel_unittest.ParallelMock():
214 failed_list = os.path.join(self.tempdir, 'list')
215 ret = upload_symbols.UploadSymbols('', breakpad_dir=self.tempdir, sleep=0,
216 retry=False, failed_list=failed_list)
217 self.assertEquals(ret, 4)
218
219 # Need to sort the output as parallel/fs discovery can be unordered.
220 got_list = sorted(osutils.ReadFile(failed_list).splitlines())
Mike Frysingerd41938e2014-02-10 06:37:55 -0500221 self.assertEquals(self.sym_paths, got_list)
222
223 def _testUpload(self, inputs, sym_paths=None):
224 """Helper for testing uploading of specific paths"""
225 if sym_paths is None:
226 sym_paths = inputs
227
228 self.upload_mock.return_value = 0
229 with parallel_unittest.ParallelMock():
230 ret = upload_symbols.UploadSymbols(sym_paths=inputs, sleep=0,
231 retry=False)
232 self.assertEquals(ret, 0)
233 self.assertEquals(self.upload_mock.call_count, len(sym_paths))
234
235 # Since upload order is arbitrary, we have to do a manual scan for each
236 # path ourselves against the uploaded file list.
237 found_syms = [x[0][1].sym_file for x in self.upload_mock.call_args_list]
238 for found_sym in found_syms:
239 for path in sym_paths:
240 if found_sym.endswith(path):
241 break
242 else:
243 raise AssertionError('Could not locate %s in %r' % (path, found_syms))
244
245 def testUploadFiles(self):
246 """Test uploading specific symbol files"""
247 sym_paths = (
248 os.path.join(self.tempdir, 'bar', 'real.sym'),
249 os.path.join(self.tempdir, 'foo', 'real.sym'),
250 )
251 self._testUpload(sym_paths)
252
253 def testUploadDirectory(self):
254 """Test uploading directory of symbol files"""
255 self._testUpload([self.tempdir], sym_paths=self.sym_paths)
256
257 def testUploadLocalTarball(self):
258 """Test uploading symbols contains in a local tarball"""
259 tarball = os.path.join(self.tempdir, 'syms.tar.gz')
260 cros_build_lib.CreateTarball(
261 'syms.tar.gz', self.tempdir, compression=cros_build_lib.COMP_GZIP,
262 inputs=('foo', 'bar', 'some'))
263 self._testUpload([tarball], sym_paths=self.sym_paths)
264
265 def testUploadRemoteTarball(self):
266 """Test uploading symbols contains in a remote tarball"""
267 # TODO: Need to figure out how to mock out lib.cache.TarballCache.
Mike Frysinger7f9be142014-01-15 02:16:42 -0500268
Mike Frysinger58312e92014-03-18 04:18:36 -0400269 def testDedupeNotifyFailure(self):
270 """Test that a dedupe server failure midway doesn't wedge things"""
271 api_mock = mock.MagicMock()
272
273 def _Contains(items):
274 """Do not dedupe anything"""
275 return items
276 api_mock.contains.side_effect = _Contains
277
278 # Use a list so the closure below can modify the value.
279 item_count = [0]
280 # Pick a number big enough to trigger a hang normally, but not so
281 # big it adds a lot of overhead.
282 item_limit = 50
283 def _Push(*_args):
284 """Die in the middle of the push list"""
285 item_count[0] += 1
286 if item_count[0] > (item_limit / 10):
287 raise ValueError('time to die')
288 api_mock.push.side_effect = _Push
289
290 self.PatchObject(isolateserver, 'get_storage_api', return_value=api_mock)
291
292 def _Uploader(*args, **kwargs):
293 """Pass the uploaded symbol to the deduper"""
294 sym_item = args[1]
295 passed_queue = kwargs['passed_queue']
296 passed_queue.put(sym_item)
297 self.upload_mock.side_effect = _Uploader
298
299 self.upload_mock.return_value = 0
300 with parallel_unittest.ParallelMock():
301 ret = upload_symbols.UploadSymbols(
302 '', sym_paths=[self.tempdir] * item_limit, sleep=0,
303 dedupe_namespace='inva!id name$pace')
304 self.assertEqual(ret, 0)
305 # This test normally passes by not hanging.
306
Aviv Keshetd1f04632014-05-09 11:33:46 -0700307 def testSlowDedupeSystem(self):
308 """Verify a slow-to-join process doesn't break things when dedupe is off"""
309 # The sleep value here is inherently a little racy, but seems to be good
310 # enough to trigger the bug on a semi-regular basis on developer systems.
311 self.PatchObject(upload_symbols, 'SymbolDeduplicatorNotify',
312 side_effect=lambda *args: time.sleep(1))
313 # Test passing means the code didn't throw an exception.
314 upload_symbols.UploadSymbols(sym_paths=[self.tempdir])
315
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400316
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500317class SymbolDeduplicatorNotifyTest(cros_test_lib.MockTestCase):
318 """Tests for SymbolDeduplicatorNotify()"""
319
320 def setUp(self):
321 self.storage_mock = self.PatchObject(isolateserver, 'get_storage_api')
322
323 def testSmoke(self):
324 """Basic run through the system."""
325 q = mock.MagicMock()
326 q.get.side_effect = (upload_symbols.FakeItem(), None,)
327 upload_symbols.SymbolDeduplicatorNotify('name', q)
328
329 def testStorageException(self):
330 """We want to just warn & move on when dedupe server fails"""
331 log_mock = self.PatchObject(cros_build_lib, 'Warning')
332 q = mock.MagicMock()
333 q.get.side_effect = (upload_symbols.FakeItem(), None,)
334 self.storage_mock.side_effect = Exception
335 upload_symbols.SymbolDeduplicatorNotify('name', q)
336 self.assertEqual(log_mock.call_count, 1)
337
338
339class SymbolDeduplicatorTest(cros_test_lib.MockTestCase):
340 """Tests for SymbolDeduplicator()"""
341
342 def setUp(self):
343 self.storage_mock = mock.MagicMock()
344 self.header_mock = self.PatchObject(
345 cros_generate_breakpad_symbols, 'ReadSymsHeader',
346 return_value=cros_generate_breakpad_symbols.SymbolHeader(
347 os='os', cpu='cpu', id='id', name='name'))
348
349 def testNoStorageOrPaths(self):
350 """We don't want to talk to the server if there's no storage or files"""
351 upload_symbols.SymbolDeduplicator(None, [])
352 upload_symbols.SymbolDeduplicator(self.storage_mock, [])
353 self.assertEqual(self.storage_mock.call_count, 0)
354 self.assertEqual(self.header_mock.call_count, 0)
355
356 def testStorageException(self):
357 """We want to just warn & move on when dedupe server fails"""
358 log_mock = self.PatchObject(cros_build_lib, 'Warning')
359 self.storage_mock.contains.side_effect = Exception('storage error')
360 sym_paths = ['/a', '/bbbbbb', '/cc.c']
361 ret = upload_symbols.SymbolDeduplicator(self.storage_mock, sym_paths)
362 self.assertEqual(log_mock.call_count, 1)
363 self.assertEqual(self.storage_mock.contains.call_count, 1)
364 self.assertEqual(self.header_mock.call_count, len(sym_paths))
365 self.assertEqual(len(ret), len(sym_paths))
366
367
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400368class UploadSymbolTest(cros_test_lib.MockTempDirTestCase):
Mike Frysingerc4ab5782013-10-02 18:14:22 -0400369 """Tests for UploadSymbol()"""
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400370
371 def setUp(self):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400372 self.sym_file = os.path.join(self.tempdir, 'foo.sym')
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500373 self.sym_item = upload_symbols.FakeItem(sym_file=self.sym_file)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400374 self.url = 'http://eatit'
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500375 self.upload_mock = self.PatchObject(upload_symbols, 'SymUpload')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400376
377 def testUploadSymbolNormal(self):
378 """Verify we try to upload on a normal file"""
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400379 osutils.Touch(self.sym_file)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500380 ret = upload_symbols.UploadSymbol(self.url, self.sym_item)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400381 self.assertEqual(ret, 0)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500382 self.upload_mock.assert_called_with(self.url, self.sym_item)
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500383 self.assertEqual(self.upload_mock.call_count, 1)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400384
385 def testUploadSymbolErrorCountExceeded(self):
386 """Verify that when the error count gets too high, we stop uploading"""
387 errors = ctypes.c_int(10000)
388 # Pass in garbage values so that we crash if num_errors isn't handled.
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500389 ret = upload_symbols.UploadSymbol(None, self.sym_item, sleep=None,
390 num_errors=errors)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400391 self.assertEqual(ret, 0)
392
Mike Frysingerc4ab5782013-10-02 18:14:22 -0400393 def testUploadRetryErrors(self, side_effect=None):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400394 """Verify that we retry errors (and eventually give up)"""
Mike Frysingerc4ab5782013-10-02 18:14:22 -0400395 if not side_effect:
396 side_effect = urllib2.HTTPError('http://', 400, 'fail', {}, None)
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500397 self.upload_mock.side_effect = side_effect
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400398 errors = ctypes.c_int()
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500399 item = upload_symbols.FakeItem(sym_file='/dev/null')
400 ret = upload_symbols.UploadSymbol(self.url, item, num_errors=errors)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400401 self.assertEqual(ret, 1)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500402 self.upload_mock.assert_called_with(self.url, item)
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500403 self.assertTrue(self.upload_mock.call_count >= upload_symbols.MAX_RETRIES)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400404
Mike Frysingerc4ab5782013-10-02 18:14:22 -0400405 def testConnectRetryErrors(self):
406 """Verify that we retry errors (and eventually give up) w/connect errors"""
407 side_effect = urllib2.URLError('foo')
408 self.testUploadRetryErrors(side_effect=side_effect)
409
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400410 def testTruncateTooBigFiles(self):
411 """Verify we shrink big files"""
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500412 def SymUpload(_url, sym_item):
413 content = osutils.ReadFile(sym_item.sym_file)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400414 self.assertEqual(content, 'some junk\n')
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500415 self.upload_mock.upload_mock.side_effect = SymUpload
416 content = '\n'.join((
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400417 'STACK CFI 1234',
418 'some junk',
419 'STACK CFI 1234',
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500420 ))
421 osutils.WriteFile(self.sym_file, content)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500422 ret = upload_symbols.UploadSymbol(self.url, self.sym_item, file_limit=1)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400423 self.assertEqual(ret, 0)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500424 # Make sure the item passed to the upload has a temp file and not the
425 # original -- only the temp one has been stripped down.
426 temp_item = self.upload_mock.call_args[0][1]
427 self.assertNotEqual(temp_item.sym_file, self.sym_item.sym_file)
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500428 self.assertEqual(self.upload_mock.call_count, 1)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400429
430 def testTruncateReallyLargeFiles(self):
431 """Verify we try to shrink really big files"""
Mike Frysinger02e92402013-11-22 16:22:02 -0500432 warn_mock = self.PatchObject(cros_build_lib, 'PrintBuildbotStepWarnings')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400433 with open(self.sym_file, 'w+b') as f:
434 f.truncate(upload_symbols.CRASH_SERVER_FILE_LIMIT + 100)
435 f.seek(0)
436 f.write('STACK CFI 1234\n\n')
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500437 ret = upload_symbols.UploadSymbol(self.url, self.sym_item)
Mike Frysinger02e92402013-11-22 16:22:02 -0500438 self.assertEqual(ret, 0)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500439 # Make sure the item passed to the upload has a temp file and not the
440 # original -- only the temp one has been truncated.
441 temp_item = self.upload_mock.call_args[0][1]
442 self.assertNotEqual(temp_item.sym_file, self.sym_item.sym_file)
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500443 self.assertEqual(self.upload_mock.call_count, 1)
Mike Frysinger02e92402013-11-22 16:22:02 -0500444 self.assertEqual(warn_mock.call_count, 1)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400445
446
Mike Frysinger094a2172013-08-14 12:54:35 -0400447class SymUploadTest(cros_test_lib.MockTempDirTestCase):
Mike Frysingerc4ab5782013-10-02 18:14:22 -0400448 """Tests for SymUpload()"""
Mike Frysinger094a2172013-08-14 12:54:35 -0400449
450 SYM_URL = 'http://localhost/post/it/here'
451 SYM_CONTENTS = """MODULE Linux arm 123-456 blkid
452PUBLIC 1471 0 main"""
453
454 def setUp(self):
455 self.sym_file = os.path.join(self.tempdir, 'test.sym')
456 osutils.WriteFile(self.sym_file, self.SYM_CONTENTS)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500457 self.sym_item = upload_symbols.SymbolItem(self.sym_file)
Mike Frysinger094a2172013-08-14 12:54:35 -0400458
459 def testPostUpload(self):
460 """Verify HTTP POST has all the fields we need"""
461 m = self.PatchObject(urllib2, 'urlopen', autospec=True)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500462 upload_symbols.SymUpload(self.SYM_URL, self.sym_item)
Mike Frysinger094a2172013-08-14 12:54:35 -0400463 self.assertEquals(m.call_count, 1)
464 req = m.call_args[0][0]
465 self.assertEquals(req.get_full_url(), self.SYM_URL)
466 data = ''.join([x for x in req.get_data()])
467
468 fields = {
469 'code_file': 'blkid',
470 'debug_file': 'blkid',
471 'debug_identifier': '123456',
472 'os': 'Linux',
473 'cpu': 'arm',
474 }
475 for key, val in fields.iteritems():
476 line = 'Content-Disposition: form-data; name="%s"\r\n' % key
477 self.assertTrue(line in data)
478 line = '%s\r\n' % val
479 self.assertTrue(line in data)
480 line = ('Content-Disposition: form-data; name="symbol_file"; '
481 'filename="test.sym"\r\n')
482 self.assertTrue(line in data)
483 self.assertTrue(self.SYM_CONTENTS in data)
484
Mike Frysinger71046662014-09-12 18:15:15 -0700485 def testTimeout(self):
486 """Verify timeouts scale based on filesize"""
487 m = self.PatchObject(urllib2, 'urlopen', autospec=True)
488 size = self.PatchObject(os.path, 'getsize')
489
490 tests = (
491 # Small files should get rounded up to the minimum timeout.
492 (10, upload_symbols.UPLOAD_MIN_TIMEOUT),
493 # A 50MiB file should take like ~4 minutes.
494 (50 * 1024 * 1024, 257),
495 )
496 for size.return_value, timeout in tests:
497 upload_symbols.SymUpload(self.SYM_URL, self.sym_item)
498 self.assertEqual(m.call_args[1]['timeout'], timeout)
499
Mike Frysinger094a2172013-08-14 12:54:35 -0400500
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500501class UtilTest(cros_test_lib.TempDirTestCase):
502 """Various tests for utility funcs."""
503
504 def testWriteQueueToFile(self):
505 """Basic test for WriteQueueToFile."""
506 listing = os.path.join(self.tempdir, 'list')
507 exp_list = [
508 'b/c.txt',
509 'foo.log',
510 'there/might/be/giants',
511 ]
512 relpath = '/a'
513
514 q = multiprocessing.Queue()
515 for f in exp_list:
516 q.put(os.path.join(relpath, f))
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500517 q.put(None)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500518 upload_symbols.WriteQueueToFile(listing, q, '/a')
519
520 got_list = osutils.ReadFile(listing).splitlines()
521 self.assertEquals(exp_list, got_list)
522
523
Mike Frysingerea838d12014-12-08 11:55:32 -0500524def main(_argv):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400525 # pylint: disable=W0212
526 # Set timeouts small so that if the unit test hangs, it won't hang for long.
527 parallel._BackgroundTask.STARTUP_TIMEOUT = 5
528 parallel._BackgroundTask.EXIT_TIMEOUT = 5
529
530 # We want to test retry behavior, so make sure we don't sleep.
531 upload_symbols.INITIAL_RETRY_DELAY = 0
532
533 # Run the tests.
Mike Frysingerba167372015-01-21 10:37:03 -0500534 cros_test_lib.main(level='info', module=__name__)