blob: 7f28926c61e4390dc90bcc5a4a0d42f9a0b841af [file] [log] [blame]
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -04001#!/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 Frysingereb753bf2013-11-22 16:05:35 -05006"""Unittests for upload_symbols.py"""
7
Mike Frysinger7f9be142014-01-15 02:16:42 -05008from __future__ import print_function
9
Mike Frysinger0a2fd922014-09-12 20:23:42 -070010import BaseHTTPServer
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040011import ctypes
Mike Frysinger06da5202014-09-26 17:30:33 -050012import errno
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040013import logging
Mike Frysinger0c0efa22014-02-09 23:32:23 -050014import multiprocessing
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040015import os
Mike Frysinger0a2fd922014-09-12 20:23:42 -070016import signal
Mike Frysinger06da5202014-09-26 17:30:33 -050017import socket
Mike Frysinger0a2fd922014-09-12 20:23:42 -070018import SocketServer
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040019import sys
Aviv Keshetd1f04632014-05-09 11:33:46 -070020import time
Mike Frysinger094a2172013-08-14 12:54:35 -040021import urllib2
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040022
23sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)),
24 '..', '..'))
Mike Frysinger02e92402013-11-22 16:22:02 -050025from chromite.lib import cros_build_lib
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040026from chromite.lib import cros_test_lib
27from chromite.lib import osutils
28from chromite.lib import parallel
29from chromite.lib import parallel_unittest
Mike Frysinger0a2fd922014-09-12 20:23:42 -070030from chromite.lib import remote_access
Mike Frysinger5e30a4b2014-02-12 20:23:04 -050031from chromite.scripts import cros_generate_breakpad_symbols
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040032from chromite.scripts import upload_symbols
33
Mike Frysinger0c0efa22014-02-09 23:32:23 -050034# TODO(build): Finish test wrapper (http://crosbug.com/37517).
35# Until then, this has to be after the chromite imports.
36import isolateserver
37import mock
38
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040039
Mike Frysinger0a2fd922014-09-12 20:23:42 -070040class SymbolServerRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
41 """HTTP handler for symbol POSTs"""
42
43 RESP_CODE = None
44 RESP_MSG = None
45
46 def do_POST(self):
47 """Handle a POST request"""
48 # Drain the data from the client. If we don't, we might write the response
49 # and close the socket before the client finishes, so they die with EPIPE.
50 clen = int(self.headers.get('Content-Length', '0'))
51 self.rfile.read(clen)
52
53 self.send_response(self.RESP_CODE, self.RESP_MSG)
54 self.end_headers()
55
56 def log_message(self, *args, **kwargs):
57 """Stub the logger as it writes to stderr"""
58 pass
59
60
61class SymbolServer(SocketServer.ThreadingTCPServer, BaseHTTPServer.HTTPServer):
62 """Simple HTTP server that forks each request"""
63
64
65class UploadSymbolsServerTest(cros_test_lib.MockTempDirTestCase):
66 """Tests for UploadSymbols() and a local HTTP server"""
67
68 SYM_CONTENTS = """MODULE Linux arm 123-456 blkid
69PUBLIC 1471 0 main"""
70
71 def SpawnServer(self, RequestHandler):
72 """Spawn a new http server"""
Mike Frysinger06da5202014-09-26 17:30:33 -050073 while True:
74 try:
75 port = remote_access.GetUnusedPort()
76 address = ('', port)
77 self.httpd = SymbolServer(address, RequestHandler)
78 break
79 except socket.error as e:
80 if e.errno == errno.EADDRINUSE:
81 continue
82 raise
Mike Frysinger0a2fd922014-09-12 20:23:42 -070083 self.server = 'http://localhost:%i' % port
Mike Frysinger0a2fd922014-09-12 20:23:42 -070084 self.httpd_pid = os.fork()
85 if self.httpd_pid == 0:
86 self.httpd.serve_forever(poll_interval=0.1)
87 sys.exit(0)
88
89 def setUp(self):
90 self.httpd_pid = None
91 self.httpd = None
92 self.server = None
93 self.sym_file = os.path.join(self.tempdir, 'test.sym')
94 osutils.WriteFile(self.sym_file, self.SYM_CONTENTS)
95
96 def tearDown(self):
97 # Only kill the server if we forked one.
98 if self.httpd_pid:
99 os.kill(self.httpd_pid, signal.SIGUSR1)
100
101 def testSuccess(self):
102 """The server returns success for all uploads"""
103 class Handler(SymbolServerRequestHandler):
104 """Always return 200"""
105 RESP_CODE = 200
106
107 self.SpawnServer(Handler)
108 ret = upload_symbols.UploadSymbols('', server=self.server, sleep=0,
109 sym_paths=[self.sym_file] * 10,
110 retry=False)
111 self.assertEqual(ret, 0)
112
113 def testError(self):
114 """The server returns errors for all uploads"""
115 class Handler(SymbolServerRequestHandler):
116 """Always return 500"""
117 RESP_CODE = 500
118 RESP_MSG = 'Internal Server Error'
119
120 self.SpawnServer(Handler)
121 ret = upload_symbols.UploadSymbols('', server=self.server, sleep=0,
122 sym_paths=[self.sym_file] * 10,
123 retry=False)
124 self.assertEqual(ret, 4)
125
126 def testHungServer(self):
127 """The server chokes, but we recover"""
128 class Handler(SymbolServerRequestHandler):
129 """All connections choke forever"""
130 def do_POST(self):
131 while True:
132 time.sleep(1000)
133
134 self.SpawnServer(Handler)
135 with mock.patch.object(upload_symbols, 'GetUploadTimeout') as m:
136 m.return_value = 0.1
137 ret = upload_symbols.UploadSymbols('', server=self.server, sleep=0,
138 sym_paths=[self.sym_file] * 10,
139 retry=False)
140 self.assertEqual(ret, 4)
141
142
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400143class UploadSymbolsTest(cros_test_lib.MockTempDirTestCase):
Mike Frysingerc4ab5782013-10-02 18:14:22 -0400144 """Tests for UploadSymbols()"""
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400145
146 def setUp(self):
147 for d in ('foo', 'bar', 'some/dir/here'):
148 d = os.path.join(self.tempdir, d)
149 osutils.SafeMakedirs(d)
150 for f in ('ignored', 'real.sym', 'no.sym.here'):
151 f = os.path.join(d, f)
152 osutils.Touch(f)
Mike Frysingerd41938e2014-02-10 06:37:55 -0500153 self.sym_paths = [
154 'bar/real.sym',
155 'foo/real.sym',
156 'some/dir/here/real.sym',
157 ]
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400158
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500159 self.upload_mock = self.PatchObject(upload_symbols, 'UploadSymbol')
160 self.PatchObject(cros_generate_breakpad_symbols, 'ReadSymsHeader',
161 return_value=cros_generate_breakpad_symbols.SymbolHeader(
162 os='os', cpu='cpu', id='id', name='name'))
163
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400164 def _testUploadURL(self, official, expected_url):
165 """Helper for checking the url used"""
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500166 self.upload_mock.return_value = 0
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400167 with parallel_unittest.ParallelMock():
Mike Frysinger02e92402013-11-22 16:22:02 -0500168 ret = upload_symbols.UploadSymbols('', official=official, retry=False,
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400169 breakpad_dir=self.tempdir, sleep=0)
170 self.assertEqual(ret, 0)
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500171 self.assertEqual(self.upload_mock.call_count, 3)
172 for call_args in self.upload_mock.call_args_list:
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500173 url, sym_item = call_args[0]
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400174 self.assertEqual(url, expected_url)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500175 self.assertTrue(sym_item.sym_file.endswith('.sym'))
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400176
177 def testOfficialUploadURL(self):
178 """Verify we upload to the real crash server for official builds"""
179 self._testUploadURL(True, upload_symbols.OFFICIAL_UPLOAD_URL)
180
181 def testUnofficialUploadURL(self):
182 """Verify we upload to the staging crash server for unofficial builds"""
183 self._testUploadURL(False, upload_symbols.STAGING_UPLOAD_URL)
184
185 def testUploadSymbolFailureSimple(self):
186 """Verify that when UploadSymbol fails, the error count is passed up"""
187 def UploadSymbol(*_args, **kwargs):
188 kwargs['num_errors'].value = 4
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500189 self.upload_mock.side_effect = UploadSymbol
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400190 with parallel_unittest.ParallelMock():
Mike Frysinger02e92402013-11-22 16:22:02 -0500191 ret = upload_symbols.UploadSymbols('', breakpad_dir=self.tempdir, sleep=0,
192 retry=False)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400193 self.assertEquals(ret, 4)
194
195 def testUploadCount(self):
196 """Verify we can limit the number of uploaded symbols"""
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500197 self.upload_mock.return_value = 0
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400198 for c in xrange(3):
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500199 self.upload_mock.reset_mock()
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400200 with parallel_unittest.ParallelMock():
201 ret = upload_symbols.UploadSymbols('', breakpad_dir=self.tempdir,
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500202 sleep=0, upload_limit=c)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400203 self.assertEquals(ret, 0)
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500204 self.assertEqual(self.upload_mock.call_count, c)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400205
Mike Frysinger7f9be142014-01-15 02:16:42 -0500206 def testFailedFileList(self):
207 """Verify the failed file list is populated with the right content"""
208 def UploadSymbol(*args, **kwargs):
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500209 kwargs['failed_queue'].put(args[1].sym_file)
Mike Frysinger7f9be142014-01-15 02:16:42 -0500210 kwargs['num_errors'].value = 4
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500211 self.upload_mock.side_effect = UploadSymbol
Mike Frysinger7f9be142014-01-15 02:16:42 -0500212 with parallel_unittest.ParallelMock():
213 failed_list = os.path.join(self.tempdir, 'list')
214 ret = upload_symbols.UploadSymbols('', breakpad_dir=self.tempdir, sleep=0,
215 retry=False, failed_list=failed_list)
216 self.assertEquals(ret, 4)
217
218 # Need to sort the output as parallel/fs discovery can be unordered.
219 got_list = sorted(osutils.ReadFile(failed_list).splitlines())
Mike Frysingerd41938e2014-02-10 06:37:55 -0500220 self.assertEquals(self.sym_paths, got_list)
221
222 def _testUpload(self, inputs, sym_paths=None):
223 """Helper for testing uploading of specific paths"""
224 if sym_paths is None:
225 sym_paths = inputs
226
227 self.upload_mock.return_value = 0
228 with parallel_unittest.ParallelMock():
229 ret = upload_symbols.UploadSymbols(sym_paths=inputs, sleep=0,
230 retry=False)
231 self.assertEquals(ret, 0)
232 self.assertEquals(self.upload_mock.call_count, len(sym_paths))
233
234 # Since upload order is arbitrary, we have to do a manual scan for each
235 # path ourselves against the uploaded file list.
236 found_syms = [x[0][1].sym_file for x in self.upload_mock.call_args_list]
237 for found_sym in found_syms:
238 for path in sym_paths:
239 if found_sym.endswith(path):
240 break
241 else:
242 raise AssertionError('Could not locate %s in %r' % (path, found_syms))
243
244 def testUploadFiles(self):
245 """Test uploading specific symbol files"""
246 sym_paths = (
247 os.path.join(self.tempdir, 'bar', 'real.sym'),
248 os.path.join(self.tempdir, 'foo', 'real.sym'),
249 )
250 self._testUpload(sym_paths)
251
252 def testUploadDirectory(self):
253 """Test uploading directory of symbol files"""
254 self._testUpload([self.tempdir], sym_paths=self.sym_paths)
255
256 def testUploadLocalTarball(self):
257 """Test uploading symbols contains in a local tarball"""
258 tarball = os.path.join(self.tempdir, 'syms.tar.gz')
259 cros_build_lib.CreateTarball(
260 'syms.tar.gz', self.tempdir, compression=cros_build_lib.COMP_GZIP,
261 inputs=('foo', 'bar', 'some'))
262 self._testUpload([tarball], sym_paths=self.sym_paths)
263
264 def testUploadRemoteTarball(self):
265 """Test uploading symbols contains in a remote tarball"""
266 # TODO: Need to figure out how to mock out lib.cache.TarballCache.
Mike Frysinger7f9be142014-01-15 02:16:42 -0500267
Mike Frysinger58312e92014-03-18 04:18:36 -0400268 def testDedupeNotifyFailure(self):
269 """Test that a dedupe server failure midway doesn't wedge things"""
270 api_mock = mock.MagicMock()
271
272 def _Contains(items):
273 """Do not dedupe anything"""
274 return items
275 api_mock.contains.side_effect = _Contains
276
277 # Use a list so the closure below can modify the value.
278 item_count = [0]
279 # Pick a number big enough to trigger a hang normally, but not so
280 # big it adds a lot of overhead.
281 item_limit = 50
282 def _Push(*_args):
283 """Die in the middle of the push list"""
284 item_count[0] += 1
285 if item_count[0] > (item_limit / 10):
286 raise ValueError('time to die')
287 api_mock.push.side_effect = _Push
288
289 self.PatchObject(isolateserver, 'get_storage_api', return_value=api_mock)
290
291 def _Uploader(*args, **kwargs):
292 """Pass the uploaded symbol to the deduper"""
293 sym_item = args[1]
294 passed_queue = kwargs['passed_queue']
295 passed_queue.put(sym_item)
296 self.upload_mock.side_effect = _Uploader
297
298 self.upload_mock.return_value = 0
299 with parallel_unittest.ParallelMock():
300 ret = upload_symbols.UploadSymbols(
301 '', sym_paths=[self.tempdir] * item_limit, sleep=0,
302 dedupe_namespace='inva!id name$pace')
303 self.assertEqual(ret, 0)
304 # This test normally passes by not hanging.
305
Aviv Keshetd1f04632014-05-09 11:33:46 -0700306 def testSlowDedupeSystem(self):
307 """Verify a slow-to-join process doesn't break things when dedupe is off"""
308 # The sleep value here is inherently a little racy, but seems to be good
309 # enough to trigger the bug on a semi-regular basis on developer systems.
310 self.PatchObject(upload_symbols, 'SymbolDeduplicatorNotify',
311 side_effect=lambda *args: time.sleep(1))
312 # Test passing means the code didn't throw an exception.
313 upload_symbols.UploadSymbols(sym_paths=[self.tempdir])
314
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400315
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500316class SymbolDeduplicatorNotifyTest(cros_test_lib.MockTestCase):
317 """Tests for SymbolDeduplicatorNotify()"""
318
319 def setUp(self):
320 self.storage_mock = self.PatchObject(isolateserver, 'get_storage_api')
321
322 def testSmoke(self):
323 """Basic run through the system."""
324 q = mock.MagicMock()
325 q.get.side_effect = (upload_symbols.FakeItem(), None,)
326 upload_symbols.SymbolDeduplicatorNotify('name', q)
327
328 def testStorageException(self):
329 """We want to just warn & move on when dedupe server fails"""
330 log_mock = self.PatchObject(cros_build_lib, 'Warning')
331 q = mock.MagicMock()
332 q.get.side_effect = (upload_symbols.FakeItem(), None,)
333 self.storage_mock.side_effect = Exception
334 upload_symbols.SymbolDeduplicatorNotify('name', q)
335 self.assertEqual(log_mock.call_count, 1)
336
337
338class SymbolDeduplicatorTest(cros_test_lib.MockTestCase):
339 """Tests for SymbolDeduplicator()"""
340
341 def setUp(self):
342 self.storage_mock = mock.MagicMock()
343 self.header_mock = self.PatchObject(
344 cros_generate_breakpad_symbols, 'ReadSymsHeader',
345 return_value=cros_generate_breakpad_symbols.SymbolHeader(
346 os='os', cpu='cpu', id='id', name='name'))
347
348 def testNoStorageOrPaths(self):
349 """We don't want to talk to the server if there's no storage or files"""
350 upload_symbols.SymbolDeduplicator(None, [])
351 upload_symbols.SymbolDeduplicator(self.storage_mock, [])
352 self.assertEqual(self.storage_mock.call_count, 0)
353 self.assertEqual(self.header_mock.call_count, 0)
354
355 def testStorageException(self):
356 """We want to just warn & move on when dedupe server fails"""
357 log_mock = self.PatchObject(cros_build_lib, 'Warning')
358 self.storage_mock.contains.side_effect = Exception('storage error')
359 sym_paths = ['/a', '/bbbbbb', '/cc.c']
360 ret = upload_symbols.SymbolDeduplicator(self.storage_mock, sym_paths)
361 self.assertEqual(log_mock.call_count, 1)
362 self.assertEqual(self.storage_mock.contains.call_count, 1)
363 self.assertEqual(self.header_mock.call_count, len(sym_paths))
364 self.assertEqual(len(ret), len(sym_paths))
365
366
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400367class UploadSymbolTest(cros_test_lib.MockTempDirTestCase):
Mike Frysingerc4ab5782013-10-02 18:14:22 -0400368 """Tests for UploadSymbol()"""
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400369
370 def setUp(self):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400371 self.sym_file = os.path.join(self.tempdir, 'foo.sym')
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500372 self.sym_item = upload_symbols.FakeItem(sym_file=self.sym_file)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400373 self.url = 'http://eatit'
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500374 self.upload_mock = self.PatchObject(upload_symbols, 'SymUpload')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400375
376 def testUploadSymbolNormal(self):
377 """Verify we try to upload on a normal file"""
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400378 osutils.Touch(self.sym_file)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500379 ret = upload_symbols.UploadSymbol(self.url, self.sym_item)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400380 self.assertEqual(ret, 0)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500381 self.upload_mock.assert_called_with(self.url, self.sym_item)
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500382 self.assertEqual(self.upload_mock.call_count, 1)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400383
384 def testUploadSymbolErrorCountExceeded(self):
385 """Verify that when the error count gets too high, we stop uploading"""
386 errors = ctypes.c_int(10000)
387 # Pass in garbage values so that we crash if num_errors isn't handled.
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500388 ret = upload_symbols.UploadSymbol(None, self.sym_item, sleep=None,
389 num_errors=errors)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400390 self.assertEqual(ret, 0)
391
Mike Frysingerc4ab5782013-10-02 18:14:22 -0400392 def testUploadRetryErrors(self, side_effect=None):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400393 """Verify that we retry errors (and eventually give up)"""
Mike Frysingerc4ab5782013-10-02 18:14:22 -0400394 if not side_effect:
395 side_effect = urllib2.HTTPError('http://', 400, 'fail', {}, None)
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500396 self.upload_mock.side_effect = side_effect
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400397 errors = ctypes.c_int()
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500398 item = upload_symbols.FakeItem(sym_file='/dev/null')
399 ret = upload_symbols.UploadSymbol(self.url, item, num_errors=errors)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400400 self.assertEqual(ret, 1)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500401 self.upload_mock.assert_called_with(self.url, item)
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500402 self.assertTrue(self.upload_mock.call_count >= upload_symbols.MAX_RETRIES)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400403
Mike Frysingerc4ab5782013-10-02 18:14:22 -0400404 def testConnectRetryErrors(self):
405 """Verify that we retry errors (and eventually give up) w/connect errors"""
406 side_effect = urllib2.URLError('foo')
407 self.testUploadRetryErrors(side_effect=side_effect)
408
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400409 def testTruncateTooBigFiles(self):
410 """Verify we shrink big files"""
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500411 def SymUpload(_url, sym_item):
412 content = osutils.ReadFile(sym_item.sym_file)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400413 self.assertEqual(content, 'some junk\n')
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500414 self.upload_mock.upload_mock.side_effect = SymUpload
415 content = '\n'.join((
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400416 'STACK CFI 1234',
417 'some junk',
418 'STACK CFI 1234',
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500419 ))
420 osutils.WriteFile(self.sym_file, content)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500421 ret = upload_symbols.UploadSymbol(self.url, self.sym_item, file_limit=1)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400422 self.assertEqual(ret, 0)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500423 # Make sure the item passed to the upload has a temp file and not the
424 # original -- only the temp one has been stripped down.
425 temp_item = self.upload_mock.call_args[0][1]
426 self.assertNotEqual(temp_item.sym_file, self.sym_item.sym_file)
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500427 self.assertEqual(self.upload_mock.call_count, 1)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400428
429 def testTruncateReallyLargeFiles(self):
430 """Verify we try to shrink really big files"""
Mike Frysinger02e92402013-11-22 16:22:02 -0500431 warn_mock = self.PatchObject(cros_build_lib, 'PrintBuildbotStepWarnings')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400432 with open(self.sym_file, 'w+b') as f:
433 f.truncate(upload_symbols.CRASH_SERVER_FILE_LIMIT + 100)
434 f.seek(0)
435 f.write('STACK CFI 1234\n\n')
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500436 ret = upload_symbols.UploadSymbol(self.url, self.sym_item)
Mike Frysinger02e92402013-11-22 16:22:02 -0500437 self.assertEqual(ret, 0)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500438 # Make sure the item passed to the upload has a temp file and not the
439 # original -- only the temp one has been truncated.
440 temp_item = self.upload_mock.call_args[0][1]
441 self.assertNotEqual(temp_item.sym_file, self.sym_item.sym_file)
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500442 self.assertEqual(self.upload_mock.call_count, 1)
Mike Frysinger02e92402013-11-22 16:22:02 -0500443 self.assertEqual(warn_mock.call_count, 1)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400444
445
Mike Frysinger094a2172013-08-14 12:54:35 -0400446class SymUploadTest(cros_test_lib.MockTempDirTestCase):
Mike Frysingerc4ab5782013-10-02 18:14:22 -0400447 """Tests for SymUpload()"""
Mike Frysinger094a2172013-08-14 12:54:35 -0400448
449 SYM_URL = 'http://localhost/post/it/here'
450 SYM_CONTENTS = """MODULE Linux arm 123-456 blkid
451PUBLIC 1471 0 main"""
452
453 def setUp(self):
454 self.sym_file = os.path.join(self.tempdir, 'test.sym')
455 osutils.WriteFile(self.sym_file, self.SYM_CONTENTS)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500456 self.sym_item = upload_symbols.SymbolItem(self.sym_file)
Mike Frysinger094a2172013-08-14 12:54:35 -0400457
458 def testPostUpload(self):
459 """Verify HTTP POST has all the fields we need"""
460 m = self.PatchObject(urllib2, 'urlopen', autospec=True)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500461 upload_symbols.SymUpload(self.SYM_URL, self.sym_item)
Mike Frysinger094a2172013-08-14 12:54:35 -0400462 self.assertEquals(m.call_count, 1)
463 req = m.call_args[0][0]
464 self.assertEquals(req.get_full_url(), self.SYM_URL)
465 data = ''.join([x for x in req.get_data()])
466
467 fields = {
468 'code_file': 'blkid',
469 'debug_file': 'blkid',
470 'debug_identifier': '123456',
471 'os': 'Linux',
472 'cpu': 'arm',
473 }
474 for key, val in fields.iteritems():
475 line = 'Content-Disposition: form-data; name="%s"\r\n' % key
476 self.assertTrue(line in data)
477 line = '%s\r\n' % val
478 self.assertTrue(line in data)
479 line = ('Content-Disposition: form-data; name="symbol_file"; '
480 'filename="test.sym"\r\n')
481 self.assertTrue(line in data)
482 self.assertTrue(self.SYM_CONTENTS in data)
483
Mike Frysinger71046662014-09-12 18:15:15 -0700484 def testTimeout(self):
485 """Verify timeouts scale based on filesize"""
486 m = self.PatchObject(urllib2, 'urlopen', autospec=True)
487 size = self.PatchObject(os.path, 'getsize')
488
489 tests = (
490 # Small files should get rounded up to the minimum timeout.
491 (10, upload_symbols.UPLOAD_MIN_TIMEOUT),
492 # A 50MiB file should take like ~4 minutes.
493 (50 * 1024 * 1024, 257),
494 )
495 for size.return_value, timeout in tests:
496 upload_symbols.SymUpload(self.SYM_URL, self.sym_item)
497 self.assertEqual(m.call_args[1]['timeout'], timeout)
498
Mike Frysinger094a2172013-08-14 12:54:35 -0400499
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500500class UtilTest(cros_test_lib.TempDirTestCase):
501 """Various tests for utility funcs."""
502
503 def testWriteQueueToFile(self):
504 """Basic test for WriteQueueToFile."""
505 listing = os.path.join(self.tempdir, 'list')
506 exp_list = [
507 'b/c.txt',
508 'foo.log',
509 'there/might/be/giants',
510 ]
511 relpath = '/a'
512
513 q = multiprocessing.Queue()
514 for f in exp_list:
515 q.put(os.path.join(relpath, f))
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500516 q.put(None)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500517 upload_symbols.WriteQueueToFile(listing, q, '/a')
518
519 got_list = osutils.ReadFile(listing).splitlines()
520 self.assertEquals(exp_list, got_list)
521
522
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400523if __name__ == '__main__':
524 # pylint: disable=W0212
525 # Set timeouts small so that if the unit test hangs, it won't hang for long.
526 parallel._BackgroundTask.STARTUP_TIMEOUT = 5
527 parallel._BackgroundTask.EXIT_TIMEOUT = 5
528
529 # We want to test retry behavior, so make sure we don't sleep.
530 upload_symbols.INITIAL_RETRY_DELAY = 0
531
532 # Run the tests.
533 cros_test_lib.main(level=logging.INFO)