blob: d8c4d6cbb03c73f9b3254e38c9a26f075e560a26 [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
12import logging
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
16import SocketServer
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040017import sys
Aviv Keshetd1f04632014-05-09 11:33:46 -070018import time
Mike Frysinger094a2172013-08-14 12:54:35 -040019import urllib2
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040020
21sys.path.insert(0, os.path.join(os.path.dirname(os.path.realpath(__file__)),
22 '..', '..'))
Mike Frysinger02e92402013-11-22 16:22:02 -050023from chromite.lib import cros_build_lib
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040024from chromite.lib import cros_test_lib
25from chromite.lib import osutils
26from chromite.lib import parallel
27from chromite.lib import parallel_unittest
Mike Frysinger0a2fd922014-09-12 20:23:42 -070028from chromite.lib import remote_access
Mike Frysinger5e30a4b2014-02-12 20:23:04 -050029from chromite.scripts import cros_generate_breakpad_symbols
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040030from chromite.scripts import upload_symbols
31
Mike Frysinger0c0efa22014-02-09 23:32:23 -050032# TODO(build): Finish test wrapper (http://crosbug.com/37517).
33# Until then, this has to be after the chromite imports.
34import isolateserver
35import mock
36
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -040037
Mike Frysinger0a2fd922014-09-12 20:23:42 -070038class SymbolServerRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
39 """HTTP handler for symbol POSTs"""
40
41 RESP_CODE = None
42 RESP_MSG = None
43
44 def do_POST(self):
45 """Handle a POST request"""
46 # Drain the data from the client. If we don't, we might write the response
47 # and close the socket before the client finishes, so they die with EPIPE.
48 clen = int(self.headers.get('Content-Length', '0'))
49 self.rfile.read(clen)
50
51 self.send_response(self.RESP_CODE, self.RESP_MSG)
52 self.end_headers()
53
54 def log_message(self, *args, **kwargs):
55 """Stub the logger as it writes to stderr"""
56 pass
57
58
59class SymbolServer(SocketServer.ThreadingTCPServer, BaseHTTPServer.HTTPServer):
60 """Simple HTTP server that forks each request"""
61
62
63class UploadSymbolsServerTest(cros_test_lib.MockTempDirTestCase):
64 """Tests for UploadSymbols() and a local HTTP server"""
65
66 SYM_CONTENTS = """MODULE Linux arm 123-456 blkid
67PUBLIC 1471 0 main"""
68
69 def SpawnServer(self, RequestHandler):
70 """Spawn a new http server"""
71 port = remote_access.GetUnusedPort()
72 address = ('', port)
73 self.server = 'http://localhost:%i' % port
74 self.httpd = SymbolServer(address, RequestHandler)
75 self.httpd_pid = os.fork()
76 if self.httpd_pid == 0:
77 self.httpd.serve_forever(poll_interval=0.1)
78 sys.exit(0)
79
80 def setUp(self):
81 self.httpd_pid = None
82 self.httpd = None
83 self.server = None
84 self.sym_file = os.path.join(self.tempdir, 'test.sym')
85 osutils.WriteFile(self.sym_file, self.SYM_CONTENTS)
86
87 def tearDown(self):
88 # Only kill the server if we forked one.
89 if self.httpd_pid:
90 os.kill(self.httpd_pid, signal.SIGUSR1)
91
92 def testSuccess(self):
93 """The server returns success for all uploads"""
94 class Handler(SymbolServerRequestHandler):
95 """Always return 200"""
96 RESP_CODE = 200
97
98 self.SpawnServer(Handler)
99 ret = upload_symbols.UploadSymbols('', server=self.server, sleep=0,
100 sym_paths=[self.sym_file] * 10,
101 retry=False)
102 self.assertEqual(ret, 0)
103
104 def testError(self):
105 """The server returns errors for all uploads"""
106 class Handler(SymbolServerRequestHandler):
107 """Always return 500"""
108 RESP_CODE = 500
109 RESP_MSG = 'Internal Server Error'
110
111 self.SpawnServer(Handler)
112 ret = upload_symbols.UploadSymbols('', server=self.server, sleep=0,
113 sym_paths=[self.sym_file] * 10,
114 retry=False)
115 self.assertEqual(ret, 4)
116
117 def testHungServer(self):
118 """The server chokes, but we recover"""
119 class Handler(SymbolServerRequestHandler):
120 """All connections choke forever"""
121 def do_POST(self):
122 while True:
123 time.sleep(1000)
124
125 self.SpawnServer(Handler)
126 with mock.patch.object(upload_symbols, 'GetUploadTimeout') as m:
127 m.return_value = 0.1
128 ret = upload_symbols.UploadSymbols('', server=self.server, sleep=0,
129 sym_paths=[self.sym_file] * 10,
130 retry=False)
131 self.assertEqual(ret, 4)
132
133
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400134class UploadSymbolsTest(cros_test_lib.MockTempDirTestCase):
Mike Frysingerc4ab5782013-10-02 18:14:22 -0400135 """Tests for UploadSymbols()"""
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400136
137 def setUp(self):
138 for d in ('foo', 'bar', 'some/dir/here'):
139 d = os.path.join(self.tempdir, d)
140 osutils.SafeMakedirs(d)
141 for f in ('ignored', 'real.sym', 'no.sym.here'):
142 f = os.path.join(d, f)
143 osutils.Touch(f)
Mike Frysingerd41938e2014-02-10 06:37:55 -0500144 self.sym_paths = [
145 'bar/real.sym',
146 'foo/real.sym',
147 'some/dir/here/real.sym',
148 ]
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400149
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500150 self.upload_mock = self.PatchObject(upload_symbols, 'UploadSymbol')
151 self.PatchObject(cros_generate_breakpad_symbols, 'ReadSymsHeader',
152 return_value=cros_generate_breakpad_symbols.SymbolHeader(
153 os='os', cpu='cpu', id='id', name='name'))
154
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400155 def _testUploadURL(self, official, expected_url):
156 """Helper for checking the url used"""
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500157 self.upload_mock.return_value = 0
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400158 with parallel_unittest.ParallelMock():
Mike Frysinger02e92402013-11-22 16:22:02 -0500159 ret = upload_symbols.UploadSymbols('', official=official, retry=False,
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400160 breakpad_dir=self.tempdir, sleep=0)
161 self.assertEqual(ret, 0)
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500162 self.assertEqual(self.upload_mock.call_count, 3)
163 for call_args in self.upload_mock.call_args_list:
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500164 url, sym_item = call_args[0]
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400165 self.assertEqual(url, expected_url)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500166 self.assertTrue(sym_item.sym_file.endswith('.sym'))
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400167
168 def testOfficialUploadURL(self):
169 """Verify we upload to the real crash server for official builds"""
170 self._testUploadURL(True, upload_symbols.OFFICIAL_UPLOAD_URL)
171
172 def testUnofficialUploadURL(self):
173 """Verify we upload to the staging crash server for unofficial builds"""
174 self._testUploadURL(False, upload_symbols.STAGING_UPLOAD_URL)
175
176 def testUploadSymbolFailureSimple(self):
177 """Verify that when UploadSymbol fails, the error count is passed up"""
178 def UploadSymbol(*_args, **kwargs):
179 kwargs['num_errors'].value = 4
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500180 self.upload_mock.side_effect = UploadSymbol
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400181 with parallel_unittest.ParallelMock():
Mike Frysinger02e92402013-11-22 16:22:02 -0500182 ret = upload_symbols.UploadSymbols('', breakpad_dir=self.tempdir, sleep=0,
183 retry=False)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400184 self.assertEquals(ret, 4)
185
186 def testUploadCount(self):
187 """Verify we can limit the number of uploaded symbols"""
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500188 self.upload_mock.return_value = 0
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400189 for c in xrange(3):
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500190 self.upload_mock.reset_mock()
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400191 with parallel_unittest.ParallelMock():
192 ret = upload_symbols.UploadSymbols('', breakpad_dir=self.tempdir,
Mike Frysinger8ec8c502014-02-10 00:19:13 -0500193 sleep=0, upload_limit=c)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400194 self.assertEquals(ret, 0)
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500195 self.assertEqual(self.upload_mock.call_count, c)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400196
Mike Frysinger7f9be142014-01-15 02:16:42 -0500197 def testFailedFileList(self):
198 """Verify the failed file list is populated with the right content"""
199 def UploadSymbol(*args, **kwargs):
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500200 kwargs['failed_queue'].put(args[1].sym_file)
Mike Frysinger7f9be142014-01-15 02:16:42 -0500201 kwargs['num_errors'].value = 4
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500202 self.upload_mock.side_effect = UploadSymbol
Mike Frysinger7f9be142014-01-15 02:16:42 -0500203 with parallel_unittest.ParallelMock():
204 failed_list = os.path.join(self.tempdir, 'list')
205 ret = upload_symbols.UploadSymbols('', breakpad_dir=self.tempdir, sleep=0,
206 retry=False, failed_list=failed_list)
207 self.assertEquals(ret, 4)
208
209 # Need to sort the output as parallel/fs discovery can be unordered.
210 got_list = sorted(osutils.ReadFile(failed_list).splitlines())
Mike Frysingerd41938e2014-02-10 06:37:55 -0500211 self.assertEquals(self.sym_paths, got_list)
212
213 def _testUpload(self, inputs, sym_paths=None):
214 """Helper for testing uploading of specific paths"""
215 if sym_paths is None:
216 sym_paths = inputs
217
218 self.upload_mock.return_value = 0
219 with parallel_unittest.ParallelMock():
220 ret = upload_symbols.UploadSymbols(sym_paths=inputs, sleep=0,
221 retry=False)
222 self.assertEquals(ret, 0)
223 self.assertEquals(self.upload_mock.call_count, len(sym_paths))
224
225 # Since upload order is arbitrary, we have to do a manual scan for each
226 # path ourselves against the uploaded file list.
227 found_syms = [x[0][1].sym_file for x in self.upload_mock.call_args_list]
228 for found_sym in found_syms:
229 for path in sym_paths:
230 if found_sym.endswith(path):
231 break
232 else:
233 raise AssertionError('Could not locate %s in %r' % (path, found_syms))
234
235 def testUploadFiles(self):
236 """Test uploading specific symbol files"""
237 sym_paths = (
238 os.path.join(self.tempdir, 'bar', 'real.sym'),
239 os.path.join(self.tempdir, 'foo', 'real.sym'),
240 )
241 self._testUpload(sym_paths)
242
243 def testUploadDirectory(self):
244 """Test uploading directory of symbol files"""
245 self._testUpload([self.tempdir], sym_paths=self.sym_paths)
246
247 def testUploadLocalTarball(self):
248 """Test uploading symbols contains in a local tarball"""
249 tarball = os.path.join(self.tempdir, 'syms.tar.gz')
250 cros_build_lib.CreateTarball(
251 'syms.tar.gz', self.tempdir, compression=cros_build_lib.COMP_GZIP,
252 inputs=('foo', 'bar', 'some'))
253 self._testUpload([tarball], sym_paths=self.sym_paths)
254
255 def testUploadRemoteTarball(self):
256 """Test uploading symbols contains in a remote tarball"""
257 # TODO: Need to figure out how to mock out lib.cache.TarballCache.
Mike Frysinger7f9be142014-01-15 02:16:42 -0500258
Mike Frysinger58312e92014-03-18 04:18:36 -0400259 def testDedupeNotifyFailure(self):
260 """Test that a dedupe server failure midway doesn't wedge things"""
261 api_mock = mock.MagicMock()
262
263 def _Contains(items):
264 """Do not dedupe anything"""
265 return items
266 api_mock.contains.side_effect = _Contains
267
268 # Use a list so the closure below can modify the value.
269 item_count = [0]
270 # Pick a number big enough to trigger a hang normally, but not so
271 # big it adds a lot of overhead.
272 item_limit = 50
273 def _Push(*_args):
274 """Die in the middle of the push list"""
275 item_count[0] += 1
276 if item_count[0] > (item_limit / 10):
277 raise ValueError('time to die')
278 api_mock.push.side_effect = _Push
279
280 self.PatchObject(isolateserver, 'get_storage_api', return_value=api_mock)
281
282 def _Uploader(*args, **kwargs):
283 """Pass the uploaded symbol to the deduper"""
284 sym_item = args[1]
285 passed_queue = kwargs['passed_queue']
286 passed_queue.put(sym_item)
287 self.upload_mock.side_effect = _Uploader
288
289 self.upload_mock.return_value = 0
290 with parallel_unittest.ParallelMock():
291 ret = upload_symbols.UploadSymbols(
292 '', sym_paths=[self.tempdir] * item_limit, sleep=0,
293 dedupe_namespace='inva!id name$pace')
294 self.assertEqual(ret, 0)
295 # This test normally passes by not hanging.
296
Aviv Keshetd1f04632014-05-09 11:33:46 -0700297 def testSlowDedupeSystem(self):
298 """Verify a slow-to-join process doesn't break things when dedupe is off"""
299 # The sleep value here is inherently a little racy, but seems to be good
300 # enough to trigger the bug on a semi-regular basis on developer systems.
301 self.PatchObject(upload_symbols, 'SymbolDeduplicatorNotify',
302 side_effect=lambda *args: time.sleep(1))
303 # Test passing means the code didn't throw an exception.
304 upload_symbols.UploadSymbols(sym_paths=[self.tempdir])
305
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400306
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500307class SymbolDeduplicatorNotifyTest(cros_test_lib.MockTestCase):
308 """Tests for SymbolDeduplicatorNotify()"""
309
310 def setUp(self):
311 self.storage_mock = self.PatchObject(isolateserver, 'get_storage_api')
312
313 def testSmoke(self):
314 """Basic run through the system."""
315 q = mock.MagicMock()
316 q.get.side_effect = (upload_symbols.FakeItem(), None,)
317 upload_symbols.SymbolDeduplicatorNotify('name', q)
318
319 def testStorageException(self):
320 """We want to just warn & move on when dedupe server fails"""
321 log_mock = self.PatchObject(cros_build_lib, 'Warning')
322 q = mock.MagicMock()
323 q.get.side_effect = (upload_symbols.FakeItem(), None,)
324 self.storage_mock.side_effect = Exception
325 upload_symbols.SymbolDeduplicatorNotify('name', q)
326 self.assertEqual(log_mock.call_count, 1)
327
328
329class SymbolDeduplicatorTest(cros_test_lib.MockTestCase):
330 """Tests for SymbolDeduplicator()"""
331
332 def setUp(self):
333 self.storage_mock = mock.MagicMock()
334 self.header_mock = self.PatchObject(
335 cros_generate_breakpad_symbols, 'ReadSymsHeader',
336 return_value=cros_generate_breakpad_symbols.SymbolHeader(
337 os='os', cpu='cpu', id='id', name='name'))
338
339 def testNoStorageOrPaths(self):
340 """We don't want to talk to the server if there's no storage or files"""
341 upload_symbols.SymbolDeduplicator(None, [])
342 upload_symbols.SymbolDeduplicator(self.storage_mock, [])
343 self.assertEqual(self.storage_mock.call_count, 0)
344 self.assertEqual(self.header_mock.call_count, 0)
345
346 def testStorageException(self):
347 """We want to just warn & move on when dedupe server fails"""
348 log_mock = self.PatchObject(cros_build_lib, 'Warning')
349 self.storage_mock.contains.side_effect = Exception('storage error')
350 sym_paths = ['/a', '/bbbbbb', '/cc.c']
351 ret = upload_symbols.SymbolDeduplicator(self.storage_mock, sym_paths)
352 self.assertEqual(log_mock.call_count, 1)
353 self.assertEqual(self.storage_mock.contains.call_count, 1)
354 self.assertEqual(self.header_mock.call_count, len(sym_paths))
355 self.assertEqual(len(ret), len(sym_paths))
356
357
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400358class UploadSymbolTest(cros_test_lib.MockTempDirTestCase):
Mike Frysingerc4ab5782013-10-02 18:14:22 -0400359 """Tests for UploadSymbol()"""
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400360
361 def setUp(self):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400362 self.sym_file = os.path.join(self.tempdir, 'foo.sym')
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500363 self.sym_item = upload_symbols.FakeItem(sym_file=self.sym_file)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400364 self.url = 'http://eatit'
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500365 self.upload_mock = self.PatchObject(upload_symbols, 'SymUpload')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400366
367 def testUploadSymbolNormal(self):
368 """Verify we try to upload on a normal file"""
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400369 osutils.Touch(self.sym_file)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500370 ret = upload_symbols.UploadSymbol(self.url, self.sym_item)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400371 self.assertEqual(ret, 0)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500372 self.upload_mock.assert_called_with(self.url, self.sym_item)
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500373 self.assertEqual(self.upload_mock.call_count, 1)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400374
375 def testUploadSymbolErrorCountExceeded(self):
376 """Verify that when the error count gets too high, we stop uploading"""
377 errors = ctypes.c_int(10000)
378 # Pass in garbage values so that we crash if num_errors isn't handled.
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500379 ret = upload_symbols.UploadSymbol(None, self.sym_item, sleep=None,
380 num_errors=errors)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400381 self.assertEqual(ret, 0)
382
Mike Frysingerc4ab5782013-10-02 18:14:22 -0400383 def testUploadRetryErrors(self, side_effect=None):
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400384 """Verify that we retry errors (and eventually give up)"""
Mike Frysingerc4ab5782013-10-02 18:14:22 -0400385 if not side_effect:
386 side_effect = urllib2.HTTPError('http://', 400, 'fail', {}, None)
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500387 self.upload_mock.side_effect = side_effect
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400388 errors = ctypes.c_int()
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500389 item = upload_symbols.FakeItem(sym_file='/dev/null')
390 ret = upload_symbols.UploadSymbol(self.url, item, num_errors=errors)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400391 self.assertEqual(ret, 1)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500392 self.upload_mock.assert_called_with(self.url, item)
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500393 self.assertTrue(self.upload_mock.call_count >= upload_symbols.MAX_RETRIES)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400394
Mike Frysingerc4ab5782013-10-02 18:14:22 -0400395 def testConnectRetryErrors(self):
396 """Verify that we retry errors (and eventually give up) w/connect errors"""
397 side_effect = urllib2.URLError('foo')
398 self.testUploadRetryErrors(side_effect=side_effect)
399
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400400 def testTruncateTooBigFiles(self):
401 """Verify we shrink big files"""
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500402 def SymUpload(_url, sym_item):
403 content = osutils.ReadFile(sym_item.sym_file)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400404 self.assertEqual(content, 'some junk\n')
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500405 self.upload_mock.upload_mock.side_effect = SymUpload
406 content = '\n'.join((
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400407 'STACK CFI 1234',
408 'some junk',
409 'STACK CFI 1234',
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500410 ))
411 osutils.WriteFile(self.sym_file, content)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500412 ret = upload_symbols.UploadSymbol(self.url, self.sym_item, file_limit=1)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400413 self.assertEqual(ret, 0)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500414 # Make sure the item passed to the upload has a temp file and not the
415 # original -- only the temp one has been stripped down.
416 temp_item = self.upload_mock.call_args[0][1]
417 self.assertNotEqual(temp_item.sym_file, self.sym_item.sym_file)
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500418 self.assertEqual(self.upload_mock.call_count, 1)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400419
420 def testTruncateReallyLargeFiles(self):
421 """Verify we try to shrink really big files"""
Mike Frysinger02e92402013-11-22 16:22:02 -0500422 warn_mock = self.PatchObject(cros_build_lib, 'PrintBuildbotStepWarnings')
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400423 with open(self.sym_file, 'w+b') as f:
424 f.truncate(upload_symbols.CRASH_SERVER_FILE_LIMIT + 100)
425 f.seek(0)
426 f.write('STACK CFI 1234\n\n')
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500427 ret = upload_symbols.UploadSymbol(self.url, self.sym_item)
Mike Frysinger02e92402013-11-22 16:22:02 -0500428 self.assertEqual(ret, 0)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500429 # Make sure the item passed to the upload has a temp file and not the
430 # original -- only the temp one has been truncated.
431 temp_item = self.upload_mock.call_args[0][1]
432 self.assertNotEqual(temp_item.sym_file, self.sym_item.sym_file)
Mike Frysinger5e30a4b2014-02-12 20:23:04 -0500433 self.assertEqual(self.upload_mock.call_count, 1)
Mike Frysinger02e92402013-11-22 16:22:02 -0500434 self.assertEqual(warn_mock.call_count, 1)
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400435
436
Mike Frysinger094a2172013-08-14 12:54:35 -0400437class SymUploadTest(cros_test_lib.MockTempDirTestCase):
Mike Frysingerc4ab5782013-10-02 18:14:22 -0400438 """Tests for SymUpload()"""
Mike Frysinger094a2172013-08-14 12:54:35 -0400439
440 SYM_URL = 'http://localhost/post/it/here'
441 SYM_CONTENTS = """MODULE Linux arm 123-456 blkid
442PUBLIC 1471 0 main"""
443
444 def setUp(self):
445 self.sym_file = os.path.join(self.tempdir, 'test.sym')
446 osutils.WriteFile(self.sym_file, self.SYM_CONTENTS)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500447 self.sym_item = upload_symbols.SymbolItem(self.sym_file)
Mike Frysinger094a2172013-08-14 12:54:35 -0400448
449 def testPostUpload(self):
450 """Verify HTTP POST has all the fields we need"""
451 m = self.PatchObject(urllib2, 'urlopen', autospec=True)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500452 upload_symbols.SymUpload(self.SYM_URL, self.sym_item)
Mike Frysinger094a2172013-08-14 12:54:35 -0400453 self.assertEquals(m.call_count, 1)
454 req = m.call_args[0][0]
455 self.assertEquals(req.get_full_url(), self.SYM_URL)
456 data = ''.join([x for x in req.get_data()])
457
458 fields = {
459 'code_file': 'blkid',
460 'debug_file': 'blkid',
461 'debug_identifier': '123456',
462 'os': 'Linux',
463 'cpu': 'arm',
464 }
465 for key, val in fields.iteritems():
466 line = 'Content-Disposition: form-data; name="%s"\r\n' % key
467 self.assertTrue(line in data)
468 line = '%s\r\n' % val
469 self.assertTrue(line in data)
470 line = ('Content-Disposition: form-data; name="symbol_file"; '
471 'filename="test.sym"\r\n')
472 self.assertTrue(line in data)
473 self.assertTrue(self.SYM_CONTENTS in data)
474
Mike Frysinger71046662014-09-12 18:15:15 -0700475 def testTimeout(self):
476 """Verify timeouts scale based on filesize"""
477 m = self.PatchObject(urllib2, 'urlopen', autospec=True)
478 size = self.PatchObject(os.path, 'getsize')
479
480 tests = (
481 # Small files should get rounded up to the minimum timeout.
482 (10, upload_symbols.UPLOAD_MIN_TIMEOUT),
483 # A 50MiB file should take like ~4 minutes.
484 (50 * 1024 * 1024, 257),
485 )
486 for size.return_value, timeout in tests:
487 upload_symbols.SymUpload(self.SYM_URL, self.sym_item)
488 self.assertEqual(m.call_args[1]['timeout'], timeout)
489
Mike Frysinger094a2172013-08-14 12:54:35 -0400490
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500491class UtilTest(cros_test_lib.TempDirTestCase):
492 """Various tests for utility funcs."""
493
494 def testWriteQueueToFile(self):
495 """Basic test for WriteQueueToFile."""
496 listing = os.path.join(self.tempdir, 'list')
497 exp_list = [
498 'b/c.txt',
499 'foo.log',
500 'there/might/be/giants',
501 ]
502 relpath = '/a'
503
504 q = multiprocessing.Queue()
505 for f in exp_list:
506 q.put(os.path.join(relpath, f))
Mike Frysinger5e6dd712014-03-07 22:21:17 -0500507 q.put(None)
Mike Frysinger0c0efa22014-02-09 23:32:23 -0500508 upload_symbols.WriteQueueToFile(listing, q, '/a')
509
510 got_list = osutils.ReadFile(listing).splitlines()
511 self.assertEquals(exp_list, got_list)
512
513
Mike Frysingerd5fcb3a2013-05-30 21:10:50 -0400514if __name__ == '__main__':
515 # pylint: disable=W0212
516 # Set timeouts small so that if the unit test hangs, it won't hang for long.
517 parallel._BackgroundTask.STARTUP_TIMEOUT = 5
518 parallel._BackgroundTask.EXIT_TIMEOUT = 5
519
520 # We want to test retry behavior, so make sure we don't sleep.
521 upload_symbols.INITIAL_RETRY_DELAY = 0
522
523 # Run the tests.
524 cros_test_lib.main(level=logging.INFO)