dev_server_unittest.py revision bf59e756d583ad90a487ab53539c41040000ae40
1#!/usr/bin/python
2#
3# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""Unit tests for client/common_lib/cros/dev_server.py."""
8
9import __builtin__
10
11import httplib
12import json
13import mox
14import os
15import StringIO
16import time
17import unittest
18import urllib2
19
20import common
21from autotest_lib.client.bin import utils as bin_utils
22from autotest_lib.client.common_lib import android_utils
23from autotest_lib.client.common_lib import error
24from autotest_lib.client.common_lib import global_config
25from autotest_lib.client.common_lib import utils
26from autotest_lib.client.common_lib.cros import dev_server
27from autotest_lib.client.common_lib.cros import retry
28
29
30def retry_mock(ExceptionToCheck, timeout_min, exception_to_raise=None,
31               label=None):
32    """A mock retry decorator to use in place of the actual one for testing.
33
34    @param ExceptionToCheck: the exception to check.
35    @param timeout_mins: Amount of time in mins to wait before timing out.
36    @param exception_to_raise: the exception to raise in retry.retry
37    @param label: used in debug messages
38
39    """
40    def inner_retry(func):
41        """The actual decorator.
42
43        @param func: Function to be called in decorator.
44
45        """
46        return func
47
48    return inner_retry
49
50
51class MockSshResponse(object):
52    """An ssh response mocked for testing."""
53
54    def __init__(self, output, exit_status=0):
55        self.stdout = output
56        self.exit_status = exit_status
57        self.stderr = 'SSH connection error occurred.'
58
59
60class MockSshError(error.CmdError):
61    """An ssh error response mocked for testing."""
62
63    def __init__(self):
64        self.result_obj = MockSshResponse('error', exit_status=255)
65
66
67E403 = urllib2.HTTPError(url='',
68                         code=httplib.FORBIDDEN,
69                         msg='Error 403',
70                         hdrs=None,
71                         fp=StringIO.StringIO('Expected.'))
72E500 = urllib2.HTTPError(url='',
73                         code=httplib.INTERNAL_SERVER_ERROR,
74                         msg='Error 500',
75                         hdrs=None,
76                         fp=StringIO.StringIO('Expected.'))
77CMD_ERROR = error.CmdError('error_cmd', MockSshError().result_obj)
78
79
80class RunCallTest(mox.MoxTestBase):
81    """Unit tests for ImageServerBase.run_call or DevServer.run_call."""
82
83    def setUp(self):
84        """Set up the test"""
85        self.test_call = 'http://nothing/test'
86        self.hostname = 'nothing'
87        self.contents = 'true'
88        self.contents_readline = ['file/one', 'file/two']
89        self.save_ssh_config = dev_server.ENABLE_SSH_CONNECTION_FOR_DEVSERVER
90        super(RunCallTest, self).setUp()
91        self.mox.StubOutWithMock(urllib2, 'urlopen')
92        self.mox.StubOutWithMock(utils, 'run')
93
94
95    def tearDown(self):
96        """Tear down the test"""
97        dev_server.ENABLE_SSH_CONNECTION_FOR_DEVSERVER = self.save_ssh_config
98        super(RunCallTest, self).tearDown()
99
100
101    def testRunCallWithSingleCallHTTP(self):
102        """Test dev_server.ImageServerBase.run_call using http with arg:
103        (call)."""
104        dev_server.ENABLE_SSH_CONNECTION_FOR_DEVSERVER = False
105
106        urllib2.urlopen(mox.StrContains(self.test_call)).AndReturn(
107                StringIO.StringIO(self.contents))
108        self.mox.ReplayAll()
109        response = dev_server.ImageServerBase.run_call(self.test_call)
110        self.assertEquals(self.contents, response)
111
112
113    def testRunCallWithCallAndReadlineHTTP(self):
114        """Test dev_server.ImageServerBase.run_call using http with arg:
115        (call, readline=True)."""
116        dev_server.ENABLE_SSH_CONNECTION_FOR_DEVSERVER = False
117
118        urllib2.urlopen(mox.StrContains(self.test_call)).AndReturn(
119                StringIO.StringIO('\n'.join(self.contents_readline)))
120        self.mox.ReplayAll()
121        response = dev_server.ImageServerBase.run_call(
122                self.test_call, readline=True)
123        self.assertEquals(self.contents_readline, response)
124
125
126    def testRunCallWithCallAndTimeoutHTTP(self):
127        """Test dev_server.ImageServerBase.run_call using http with args:
128        (call, timeout=xxx)."""
129        dev_server.ENABLE_SSH_CONNECTION_FOR_DEVSERVER = False
130
131        urllib2.urlopen(mox.StrContains(self.test_call), data=None).AndReturn(
132                StringIO.StringIO(self.contents))
133        self.mox.ReplayAll()
134        response = dev_server.ImageServerBase.run_call(
135                self.test_call, timeout=60)
136        self.assertEquals(self.contents, response)
137
138
139    def testRunCallWithSingleCallSSH(self):
140        """Test dev_server.ImageServerBase.run_call using ssh with arg:
141        (call)."""
142        dev_server.ENABLE_SSH_CONNECTION_FOR_DEVSERVER = True
143        self.mox.StubOutWithMock(utils, 'get_restricted_subnet')
144        utils.get_restricted_subnet(
145                self.hostname, utils.RESTRICTED_SUBNETS).AndReturn(
146                self.hostname)
147
148        to_return = MockSshResponse(self.contents)
149        utils.run(mox.StrContains(self.test_call),
150                  timeout=mox.IgnoreArg()).AndReturn(to_return)
151        self.mox.ReplayAll()
152        response = dev_server.ImageServerBase.run_call(self.test_call)
153        self.assertEquals(self.contents, response)
154
155
156    def testRunCallWithCallAndReadlineSSH(self):
157        """Test dev_server.ImageServerBase.run_call using ssh with args:
158        (call, readline=True)."""
159        dev_server.ENABLE_SSH_CONNECTION_FOR_DEVSERVER = True
160        self.mox.StubOutWithMock(utils, 'get_restricted_subnet')
161        utils.get_restricted_subnet(
162                self.hostname, utils.RESTRICTED_SUBNETS).AndReturn(
163                self.hostname)
164
165        to_return = MockSshResponse('\n'.join(self.contents_readline))
166        utils.run(mox.StrContains(self.test_call),
167                  timeout=mox.IgnoreArg()).AndReturn(to_return)
168        self.mox.ReplayAll()
169        response = dev_server.ImageServerBase.run_call(
170                self.test_call, readline=True)
171        self.assertEquals(self.contents_readline, response)
172
173
174    def testRunCallWithCallAndTimeoutSSH(self):
175        """Test dev_server.ImageServerBase.run_call using ssh with args:
176        (call, timeout=xxx)."""
177        dev_server.ENABLE_SSH_CONNECTION_FOR_DEVSERVER = True
178        self.mox.StubOutWithMock(utils, 'get_restricted_subnet')
179        utils.get_restricted_subnet(
180                self.hostname, utils.RESTRICTED_SUBNETS).AndReturn(
181                self.hostname)
182
183        to_return = MockSshResponse(self.contents)
184        utils.run(mox.StrContains(self.test_call),
185                  timeout=mox.IgnoreArg()).AndReturn(to_return)
186        self.mox.ReplayAll()
187        response = dev_server.ImageServerBase.run_call(
188                self.test_call, timeout=60)
189        self.assertEquals(self.contents, response)
190
191
192    def testRunCallWithExceptionHTTP(self):
193        """Test dev_server.ImageServerBase.run_call using http with raising
194        exception."""
195        dev_server.ENABLE_SSH_CONNECTION_FOR_DEVSERVER = False
196        urllib2.urlopen(mox.StrContains(self.test_call)).AndRaise(E500)
197        self.mox.ReplayAll()
198        self.assertRaises(urllib2.HTTPError,
199                          dev_server.ImageServerBase.run_call,
200                          self.test_call)
201
202
203    def testRunCallWithExceptionSSH(self):
204        """Test dev_server.ImageServerBase.run_call using ssh with raising
205        exception."""
206        dev_server.ENABLE_SSH_CONNECTION_FOR_DEVSERVER = True
207        self.mox.StubOutWithMock(utils, 'get_restricted_subnet')
208        utils.get_restricted_subnet(
209                self.hostname, utils.RESTRICTED_SUBNETS).AndReturn(
210                self.hostname)
211
212        utils.run(mox.StrContains(self.test_call),
213                  timeout=mox.IgnoreArg()).AndRaise(MockSshError())
214        self.mox.ReplayAll()
215        self.assertRaises(error.CmdError,
216                          dev_server.ImageServerBase.run_call,
217                          self.test_call)
218
219
220    def testRunCallByDevServerHTTP(self):
221        """Test dev_server.DevServer.run_call, which uses http, and can be
222        directly called by CrashServer."""
223        urllib2.urlopen(
224                mox.StrContains(self.test_call), data=None).AndReturn(
225                        StringIO.StringIO(self.contents))
226        self.mox.ReplayAll()
227        response = dev_server.DevServer.run_call(
228               self.test_call, timeout=60)
229        self.assertEquals(self.contents, response)
230
231
232class DevServerTest(mox.MoxTestBase):
233    """Unit tests for dev_server.DevServer.
234
235    @var _HOST: fake dev server host address.
236    """
237
238    _HOST = 'http://nothing'
239    _CRASH_HOST = 'http://nothing-crashed'
240    _CONFIG = global_config.global_config
241
242
243    def setUp(self):
244        """Set up the test"""
245        super(DevServerTest, self).setUp()
246        self.crash_server = dev_server.CrashServer(DevServerTest._CRASH_HOST)
247        self.dev_server = dev_server.ImageServer(DevServerTest._HOST)
248        self.android_dev_server = dev_server.AndroidBuildServer(
249                DevServerTest._HOST)
250        self.mox.StubOutWithMock(dev_server.ImageServerBase, 'run_call')
251        self.mox.StubOutWithMock(urllib2, 'urlopen')
252        self.mox.StubOutWithMock(utils, 'run')
253        self.mox.StubOutWithMock(os.path, 'exists')
254        # Hide local restricted_subnets setting.
255        dev_server.RESTRICTED_SUBNETS = []
256
257
258    def testSimpleResolve(self):
259        """One devserver, verify we resolve to it."""
260        self.mox.StubOutWithMock(dev_server, '_get_dev_server_list')
261        self.mox.StubOutWithMock(dev_server.ImageServer, 'devserver_healthy')
262        dev_server._get_dev_server_list().MultipleTimes().AndReturn(
263                [DevServerTest._HOST])
264        dev_server.ImageServer.devserver_healthy(DevServerTest._HOST).AndReturn(
265                                                                        True)
266        self.mox.ReplayAll()
267        devserver = dev_server.ImageServer.resolve('my_build')
268        self.assertEquals(devserver.url(), DevServerTest._HOST)
269
270
271    def testResolveWithFailure(self):
272        """Ensure we rehash on a failed ping on a bad_host."""
273        self.mox.StubOutWithMock(dev_server, '_get_dev_server_list')
274        bad_host, good_host = 'http://bad_host:99', 'http://good_host:8080'
275        dev_server._get_dev_server_list().MultipleTimes().AndReturn(
276                [bad_host, good_host])
277        argument1 = mox.StrContains(bad_host)
278        argument2 = mox.StrContains(good_host)
279
280        # Mock out bad ping failure to bad_host by raising devserver exception.
281        dev_server.ImageServerBase.run_call(
282                argument1, timeout=mox.IgnoreArg()).AndRaise(
283                        dev_server.DevServerException())
284        # Good host is good.
285        dev_server.ImageServerBase.run_call(
286                argument2, timeout=mox.IgnoreArg()).AndReturn(
287                        '{"free_disk": 1024}')
288
289        self.mox.ReplayAll()
290        host = dev_server.ImageServer.resolve(0) # Using 0 as it'll hash to 0.
291        self.assertEquals(host.url(), good_host)
292        self.mox.VerifyAll()
293
294
295    def testResolveWithFailureURLError(self):
296        """Ensure we rehash on a failed ping using http on a bad_host after
297        urlerror."""
298        # Set retry.retry to retry_mock for just returning the original
299        # method for this test. This is to save waiting time for real retry,
300        # which is defined by dev_server.DEVSERVER_SSH_TIMEOUT_MINS.
301        # Will reset retry.retry to real retry at the end of this test.
302        real_retry = retry.retry
303        retry.retry = retry_mock
304
305        self.mox.StubOutWithMock(dev_server, '_get_dev_server_list')
306        bad_host, good_host = 'http://bad_host:99', 'http://good_host:8080'
307        dev_server._get_dev_server_list().MultipleTimes().AndReturn(
308                [bad_host, good_host])
309        argument1 = mox.StrContains(bad_host)
310        argument2 = mox.StrContains(good_host)
311
312        # Mock out bad ping failure to bad_host by raising devserver exception.
313        dev_server.ImageServerBase.run_call(
314                argument1, timeout=mox.IgnoreArg()).MultipleTimes().AndRaise(
315                        urllib2.URLError('urlopen connection timeout'))
316
317        # Good host is good.
318        dev_server.ImageServerBase.run_call(
319                argument2, timeout=mox.IgnoreArg()).AndReturn(
320                        '{"free_disk": 1024}')
321
322        self.mox.ReplayAll()
323        host = dev_server.ImageServer.resolve(0) # Using 0 as it'll hash to 0.
324        self.assertEquals(host.url(), good_host)
325        self.mox.VerifyAll()
326
327        retry.retry = real_retry
328
329
330    def testResolveWithManyDevservers(self):
331        """Should be able to return different urls with multiple devservers."""
332        self.mox.StubOutWithMock(dev_server.ImageServer, 'servers')
333        self.mox.StubOutWithMock(dev_server.DevServer, 'devserver_healthy')
334
335        host0_expected = 'http://host0:8080'
336        host1_expected = 'http://host1:8082'
337
338        dev_server.ImageServer.servers().MultipleTimes().AndReturn(
339                [host0_expected, host1_expected])
340        dev_server.ImageServer.devserver_healthy(host0_expected).AndReturn(True)
341        dev_server.ImageServer.devserver_healthy(host1_expected).AndReturn(True)
342
343        self.mox.ReplayAll()
344        host0 = dev_server.ImageServer.resolve(0)
345        host1 = dev_server.ImageServer.resolve(1)
346        self.mox.VerifyAll()
347
348        self.assertEqual(host0.url(), host0_expected)
349        self.assertEqual(host1.url(), host1_expected)
350
351
352    def testCmdErrorRetryCollectAULog(self):
353        """Devserver should retry _collect_au_log() on CMDError,
354        but pass through real exception."""
355        dev_server.ImageServerBase.run_call(
356                mox.IgnoreArg()).AndRaise(CMD_ERROR)
357        dev_server.ImageServerBase.run_call(
358                mox.IgnoreArg()).AndRaise(E500)
359        self.mox.ReplayAll()
360        self.assertFalse(self.dev_server.collect_au_log(
361                '100.0.0.0', 100, 'path/'))
362
363
364    def testURLErrorRetryCollectAULog(self):
365        """Devserver should retry _collect_au_log() on URLError,
366        but pass through real exception."""
367        self.mox.StubOutWithMock(time, 'sleep')
368
369        refused = urllib2.URLError('[Errno 111] Connection refused')
370        dev_server.ImageServerBase.run_call(
371                mox.IgnoreArg()).AndRaise(refused)
372        time.sleep(mox.IgnoreArg())
373        dev_server.ImageServerBase.run_call(mox.IgnoreArg()).AndRaise(E403)
374        self.mox.ReplayAll()
375        self.assertFalse(self.dev_server.collect_au_log(
376                '100.0.0.0', 100, 'path/'))
377
378
379    def testCmdErrorRetryKillAUProcess(self):
380        """Devserver should retry _kill_au_process() on CMDError,
381        but pass through real exception."""
382        dev_server.ImageServerBase.run_call(
383                mox.IgnoreArg()).AndRaise(CMD_ERROR)
384        dev_server.ImageServerBase.run_call(
385                mox.IgnoreArg()).AndRaise(E500)
386        self.mox.ReplayAll()
387        self.assertFalse(self.dev_server.kill_au_process_for_host(
388                '100.0.0.0', 100))
389
390
391    def testURLErrorRetryKillAUProcess(self):
392        """Devserver should retry _kill_au_process() on URLError,
393        but pass through real exception."""
394        self.mox.StubOutWithMock(time, 'sleep')
395
396        refused = urllib2.URLError('[Errno 111] Connection refused')
397        dev_server.ImageServerBase.run_call(
398                mox.IgnoreArg()).AndRaise(refused)
399        time.sleep(mox.IgnoreArg())
400        dev_server.ImageServerBase.run_call(mox.IgnoreArg()).AndRaise(E403)
401        self.mox.ReplayAll()
402        self.assertFalse(self.dev_server.kill_au_process_for_host(
403                '100.0.0.0', 100))
404
405
406    def testCmdErrorRetryCleanTrackLog(self):
407        """Devserver should retry _clean_track_log() on CMDError,
408        but pass through real exception."""
409        dev_server.ImageServerBase.run_call(
410                mox.IgnoreArg()).AndRaise(CMD_ERROR)
411        dev_server.ImageServerBase.run_call(
412                mox.IgnoreArg()).AndRaise(E500)
413        self.mox.ReplayAll()
414        self.assertFalse(self.dev_server.clean_track_log('100.0.0.0', 100))
415
416
417    def testURLErrorRetryCleanTrackLog(self):
418        """Devserver should retry _clean_track_log() on URLError,
419        but pass through real exception."""
420        self.mox.StubOutWithMock(time, 'sleep')
421
422        refused = urllib2.URLError('[Errno 111] Connection refused')
423        dev_server.ImageServerBase.run_call(
424                mox.IgnoreArg()).AndRaise(refused)
425        time.sleep(mox.IgnoreArg())
426        dev_server.ImageServerBase.run_call(mox.IgnoreArg()).AndRaise(E403)
427        self.mox.ReplayAll()
428        self.assertFalse(self.dev_server.clean_track_log('100.0.0.0', 100))
429
430
431    def _mockWriteFile(self):
432        """Mock write content to a file."""
433        mock_file = self.mox.CreateMockAnything()
434        open(mox.IgnoreArg(), 'w').AndReturn(mock_file)
435        mock_file.__enter__().AndReturn(mock_file)
436        mock_file.write(mox.IgnoreArg())
437        mock_file.__exit__(None, None, None)
438
439
440    def _preSetupForAutoUpdate(self, **kwargs):
441        """Pre-setup for testing auto_update logics and error handling in
442        devserver."""
443        response1 = (True, 100)
444        response2 = (True, 'Completed')
445        argument1 = mox.And(mox.StrContains(self._HOST),
446                            mox.StrContains('cros_au'))
447        argument2 = mox.And(mox.StrContains(self._HOST),
448                            mox.StrContains('get_au_status'))
449        argument3 = mox.And(mox.StrContains(self._HOST),
450                            mox.StrContains('handler_cleanup'))
451        argument4 = mox.And(mox.StrContains(self._HOST),
452                            mox.StrContains('collect_cros_au_log'))
453        argument5 = mox.And(mox.StrContains(self._HOST),
454                            mox.StrContains('kill_au_proc'))
455
456        retry_error = None
457        if 'retry_error' in kwargs:
458            retry_error = kwargs['retry_error']
459
460        raised_error = E403
461        if 'raised_error' in kwargs:
462            raised_error = kwargs['raised_error']
463
464        if 'cros_au_error' in kwargs:
465            if retry_error:
466                dev_server.ImageServerBase.run_call(argument1).AndRaise(
467                        retry_error)
468                time.sleep(mox.IgnoreArg())
469
470            if kwargs['cros_au_error']:
471                dev_server.ImageServerBase.run_call(argument1).AndRaise(
472                        raised_error)
473            else:
474                dev_server.ImageServerBase.run_call(argument1).AndReturn(
475                        json.dumps(response1))
476
477        if 'get_au_status_error' in kwargs:
478            if retry_error:
479                dev_server.ImageServerBase.run_call(argument2).AndRaise(
480                        retry_error)
481                time.sleep(mox.IgnoreArg())
482
483            if kwargs['get_au_status_error']:
484                dev_server.ImageServerBase.run_call(argument2).AndRaise(
485                        raised_error)
486            else:
487                dev_server.ImageServerBase.run_call(argument2).AndReturn(
488                        json.dumps(response2))
489
490        if 'handler_cleanup_error' in kwargs:
491            if kwargs['handler_cleanup_error']:
492                dev_server.ImageServerBase.run_call(argument3).AndRaise(
493                        raised_error)
494            else:
495                dev_server.ImageServerBase.run_call(argument3).AndReturn('True')
496
497        if 'collect_au_log_error' in kwargs:
498            if kwargs['collect_au_log_error']:
499                dev_server.ImageServerBase.run_call(argument4).AndRaise(
500                        raised_error)
501            else:
502                dev_server.ImageServerBase.run_call(argument4).AndReturn('log')
503                os.path.exists(mox.IgnoreArg()).AndReturn(True)
504                self._mockWriteFile()
505
506        if 'kill_au_proc_error' in kwargs:
507            if kwargs['kill_au_proc_error']:
508                dev_server.ImageServerBase.run_call(argument5).AndRaise(
509                        raised_error)
510            else:
511                dev_server.ImageServerBase.run_call(argument5).AndReturn('True')
512
513
514    def testSuccessfulTriggerAutoUpdate(self):
515        """Verify the dev server's auto_update() succeeds."""
516        kwargs={'cros_au_error': False, 'get_au_status_error': False,
517                'handler_cleanup_error': False}
518        self._preSetupForAutoUpdate(**kwargs)
519
520        self.mox.ReplayAll()
521        self.dev_server.auto_update('100.0.0.0', '')
522        self.mox.VerifyAll()
523
524
525    def testSuccessfulTriggerAutoUpdateWithCollectingLog(self):
526        """Verify the dev server's auto_update() with collecting logs
527        succeeds."""
528        kwargs={'cros_au_error': False, 'get_au_status_error': False,
529                'handler_cleanup_error': False, 'collect_au_log_error': False}
530        self.mox.StubOutWithMock(__builtin__, 'open')
531        self._preSetupForAutoUpdate(**kwargs)
532
533        self.mox.ReplayAll()
534        self.dev_server.auto_update('100.0.0.0', '', log_dir='path/')
535        self.mox.VerifyAll()
536
537
538    def testCrOSAUURLErrorRetryTriggerAutoUpdateSucceed(self):
539        """Devserver should retry cros_au() on URLError."""
540        self.mox.StubOutWithMock(time, 'sleep')
541        refused = urllib2.URLError('[Errno 111] Connection refused')
542        kwargs={'retry_error': refused, 'cros_au_error': False,
543                'get_au_status_error': False, 'handler_cleanup_error': False,
544                'collect_au_log_error': False}
545        self.mox.StubOutWithMock(__builtin__, 'open')
546        self._preSetupForAutoUpdate(**kwargs)
547
548        self.mox.ReplayAll()
549        self.dev_server.auto_update('100.0.0.0', '', log_dir='path/')
550        self.mox.VerifyAll()
551
552
553    def testCrOSAUCmdErrorRetryTriggerAutoUpdateSucceed(self):
554        """Devserver should retry cros_au() on CMDError."""
555        self.mox.StubOutWithMock(time, 'sleep')
556        self.mox.StubOutWithMock(__builtin__, 'open')
557        kwargs={'retry_error': CMD_ERROR, 'cros_au_error': False,
558                'get_au_status_error': False, 'handler_cleanup_error': False,
559                'collect_au_log_error': False}
560        self._preSetupForAutoUpdate(**kwargs)
561
562        self.mox.ReplayAll()
563        self.dev_server.auto_update('100.0.0.0', '', log_dir='path/')
564        self.mox.VerifyAll()
565
566
567    def testCrOSAUURLErrorRetryTriggerAutoUpdateFail(self):
568        """Devserver should retry cros_au() on URLError, but pass through
569        real exception."""
570        self.mox.StubOutWithMock(time, 'sleep')
571        refused = urllib2.URLError('[Errno 111] Connection refused')
572        kwargs={'retry_error': refused, 'cros_au_error': True,
573                'raised_error': E500}
574
575        for i in range(dev_server.AU_RETRY_LIMIT):
576            self._preSetupForAutoUpdate(**kwargs)
577            if i < dev_server.AU_RETRY_LIMIT - 1:
578                time.sleep(mox.IgnoreArg())
579
580        self.mox.ReplayAll()
581        self.assertRaises(dev_server.DevServerException,
582                          self.dev_server.auto_update,
583                          '', '')
584
585
586    def testCrOSAUCmdErrorRetryTriggerAutoUpdateFail(self):
587        """Devserver should retry cros_au() on CMDError, but pass through
588        real exception."""
589        self.mox.StubOutWithMock(time, 'sleep')
590        kwargs={'retry_error': CMD_ERROR, 'cros_au_error': True}
591
592        for i in range(dev_server.AU_RETRY_LIMIT):
593            self._preSetupForAutoUpdate(**kwargs)
594            if i < dev_server.AU_RETRY_LIMIT - 1:
595                time.sleep(mox.IgnoreArg())
596
597        self.mox.ReplayAll()
598        self.assertRaises(dev_server.DevServerException,
599                          self.dev_server.auto_update,
600                          '', '')
601
602
603    def testGetAUStatusErrorInAutoUpdate(self):
604        """Verify devserver's auto_update() logics for handling get_au_status
605        errors.
606
607        Func auto_update() should call 'handler_cleanup' and 'collect_au_log'
608        even if '_trigger_auto_update()' failed.
609        """
610        self.mox.StubOutWithMock(time, 'sleep')
611        self.mox.StubOutWithMock(__builtin__, 'open')
612        kwargs={'cros_au_error': False, 'get_au_status_error': True,
613                'handler_cleanup_error': False, 'collect_au_log_error': False,
614                'kill_au_proc_error': False}
615
616        for i in range(dev_server.AU_RETRY_LIMIT):
617            self._preSetupForAutoUpdate(**kwargs)
618            if i < dev_server.AU_RETRY_LIMIT - 1:
619                time.sleep(mox.IgnoreArg())
620
621        self.mox.ReplayAll()
622        self.assertRaises(dev_server.DevServerException,
623                          self.dev_server.auto_update,
624                          '100.0.0.0', 'build', log_dir='path/')
625
626
627    def testCleanUpErrorInAutoUpdate(self):
628        """Verify devserver's auto_update() logics for handling handler_cleanup
629        errors.
630
631        Func auto_update() should call 'handler_cleanup' and 'collect_au_log'
632        no matter '_trigger_auto_update()' succeeds or fails.
633        """
634        self.mox.StubOutWithMock(time, 'sleep')
635        self.mox.StubOutWithMock(__builtin__, 'open')
636        kwargs={'cros_au_error': False, 'get_au_status_error': False,
637                'handler_cleanup_error': True, 'collect_au_log_error': False,
638                'kill_au_proc_error': False}
639
640
641        for i in range(dev_server.AU_RETRY_LIMIT):
642            self._preSetupForAutoUpdate(**kwargs)
643            if i < dev_server.AU_RETRY_LIMIT - 1:
644                time.sleep(mox.IgnoreArg())
645
646        self.mox.ReplayAll()
647        self.assertRaises(dev_server.DevServerException,
648                          self.dev_server.auto_update,
649                          '100.0.0.0', 'build', log_dir='path/')
650
651
652    def testCollectLogErrorInAutoUpdate(self):
653        """Verify devserver's auto_update() logics for handling collect_au_log
654        errors."""
655        self.mox.StubOutWithMock(time, 'sleep')
656        kwargs={'cros_au_error': False, 'get_au_status_error': False,
657                'handler_cleanup_error': False, 'collect_au_log_error': True,
658                'kill_au_proc_error': False}
659
660
661        for i in range(dev_server.AU_RETRY_LIMIT):
662            self._preSetupForAutoUpdate(**kwargs)
663            if i < dev_server.AU_RETRY_LIMIT - 1:
664                time.sleep(mox.IgnoreArg())
665
666        self.mox.ReplayAll()
667        self.assertRaises(dev_server.DevServerException,
668                          self.dev_server.auto_update,
669                          '100.0.0.0', 'build', log_dir='path/')
670
671
672    def testGetAUStatusErrorAndCleanUpErrorInAutoUpdate(self):
673        """Verify devserver's auto_update() logics for handling get_au_status
674        and handler_cleanup errors.
675
676        Func auto_update() should call 'handler_cleanup' and 'collect_au_log'
677        even if '_trigger_auto_update()' fails.
678        """
679        self.mox.StubOutWithMock(time, 'sleep')
680        self.mox.StubOutWithMock(__builtin__, 'open')
681        kwargs={'cros_au_error': False, 'get_au_status_error': True,
682                'handler_cleanup_error': True, 'collect_au_log_error': False,
683                'kill_au_proc_error': False}
684
685
686        for i in range(dev_server.AU_RETRY_LIMIT):
687            self._preSetupForAutoUpdate(**kwargs)
688            if i < dev_server.AU_RETRY_LIMIT - 1:
689                time.sleep(mox.IgnoreArg())
690
691        self.mox.ReplayAll()
692        self.assertRaises(dev_server.DevServerException,
693                          self.dev_server.auto_update,
694                          '100.0.0.0', 'build', log_dir='path/')
695
696
697    def testGetAUStatusErrorAndCleanUpErrorAndCollectLogErrorInAutoUpdate(self):
698        """Verify devserver's auto_update() logics for handling get_au_status,
699        handler_cleanup, and collect_au_log errors.
700
701        Func auto_update() should call 'handler_cleanup' and 'collect_au_log'
702        even if '_trigger_auto_update()' fails.
703        """
704        self.mox.StubOutWithMock(time, 'sleep')
705        kwargs={'cros_au_error': False, 'get_au_status_error': True,
706                'handler_cleanup_error': True, 'collect_au_log_error': True,
707                'kill_au_proc_error': False}
708
709        for i in range(dev_server.AU_RETRY_LIMIT):
710            self._preSetupForAutoUpdate(**kwargs)
711            if i < dev_server.AU_RETRY_LIMIT - 1:
712                time.sleep(mox.IgnoreArg())
713
714        self.mox.ReplayAll()
715        self.assertRaises(dev_server.DevServerException,
716                          self.dev_server.auto_update,
717                          '100.0.0.0', 'build', log_dir='path/')
718
719
720    def testGetAUStatusErrorAndCleanUpErrorAndCollectLogErrorAndKillErrorInAutoUpdate(self):
721        """Verify devserver's auto_update() logics for handling get_au_status,
722        handler_cleanup, collect_au_log, and kill_au_proc errors.
723
724        Func auto_update() should call 'handler_cleanup' and 'collect_au_log'
725        even if '_trigger_auto_update()' fails.
726        """
727        self.mox.StubOutWithMock(time, 'sleep')
728
729        kwargs={'cros_au_error': False, 'get_au_status_error': True,
730                'handler_cleanup_error': True, 'collect_au_log_error': True,
731                'kill_au_proc_error': True}
732
733        for i in range(dev_server.AU_RETRY_LIMIT):
734            self._preSetupForAutoUpdate(**kwargs)
735            if i < dev_server.AU_RETRY_LIMIT - 1:
736                time.sleep(mox.IgnoreArg())
737
738        self.mox.ReplayAll()
739        self.assertRaises(dev_server.DevServerException,
740                          self.dev_server.auto_update,
741                          '100.0.0.0', 'build', log_dir='path/')
742
743
744    def testSuccessfulTriggerDownloadSync(self):
745        """Call the dev server's download method with synchronous=True."""
746        name = 'fake/image'
747        self.mox.StubOutWithMock(dev_server.ImageServer, '_finish_download')
748        argument1 = mox.And(mox.StrContains(self._HOST), mox.StrContains(name),
749                            mox.StrContains('stage?'))
750        argument2 = mox.And(mox.StrContains(self._HOST), mox.StrContains(name),
751                            mox.StrContains('is_staged'))
752        dev_server.ImageServerBase.run_call(argument1).AndReturn('Success')
753        dev_server.ImageServerBase.run_call(argument2).AndReturn('True')
754        self.dev_server._finish_download(name, mox.IgnoreArg(), mox.IgnoreArg())
755
756        # Synchronous case requires a call to finish download.
757        self.mox.ReplayAll()
758        self.dev_server.trigger_download(name, synchronous=True)
759        self.mox.VerifyAll()
760
761
762    def testSuccessfulTriggerDownloadASync(self):
763        """Call the dev server's download method with synchronous=False."""
764        name = 'fake/image'
765        argument1 = mox.And(mox.StrContains(self._HOST), mox.StrContains(name),
766                            mox.StrContains('stage?'))
767        argument2 = mox.And(mox.StrContains(self._HOST), mox.StrContains(name),
768                            mox.StrContains('is_staged'))
769        dev_server.ImageServerBase.run_call(argument1).AndReturn('Success')
770        dev_server.ImageServerBase.run_call(argument2).AndReturn('True')
771
772        self.mox.ReplayAll()
773        self.dev_server.trigger_download(name, synchronous=False)
774        self.mox.VerifyAll()
775
776
777    def testURLErrorRetryTriggerDownload(self):
778        """Should retry on URLError, but pass through real exception."""
779        self.mox.StubOutWithMock(time, 'sleep')
780
781        refused = urllib2.URLError('[Errno 111] Connection refused')
782        dev_server.ImageServerBase.run_call(
783                mox.IgnoreArg()).AndRaise(refused)
784        time.sleep(mox.IgnoreArg())
785        dev_server.ImageServerBase.run_call(mox.IgnoreArg()).AndRaise(E403)
786        self.mox.ReplayAll()
787        self.assertRaises(dev_server.DevServerException,
788                          self.dev_server.trigger_download,
789                          '')
790
791
792    def testErrorTriggerDownload(self):
793        """Should call the dev server's download method using http, fail
794        gracefully."""
795        dev_server.ImageServerBase.run_call(mox.IgnoreArg()).AndRaise(E500)
796        self.mox.ReplayAll()
797        self.assertRaises(dev_server.DevServerException,
798                          self.dev_server.trigger_download,
799                          '')
800
801
802    def testForbiddenTriggerDownload(self):
803        """Should call the dev server's download method using http,
804        get exception."""
805        dev_server.ImageServerBase.run_call(mox.IgnoreArg()).AndRaise(E403)
806        self.mox.ReplayAll()
807        self.assertRaises(dev_server.DevServerException,
808                          self.dev_server.trigger_download,
809                          '')
810
811
812    def testCmdErrorTriggerDownload(self):
813        """Should call the dev server's download method using ssh, retry
814        trigger_download when getting error.CmdError, raise exception for
815        urllib2.HTTPError."""
816        dev_server.ImageServerBase.run_call(
817                mox.IgnoreArg()).AndRaise(CMD_ERROR)
818        dev_server.ImageServerBase.run_call(
819                mox.IgnoreArg()).AndRaise(E500)
820        self.mox.ReplayAll()
821        self.assertRaises(dev_server.DevServerException,
822                          self.dev_server.trigger_download,
823                          '')
824
825
826    def testSuccessfulFinishDownload(self):
827        """Should successfully call the dev server's finish download method."""
828        name = 'fake/image'
829        argument1 = mox.And(mox.StrContains(self._HOST),
830                            mox.StrContains(name),
831                            mox.StrContains('stage?'))
832        argument2 = mox.And(mox.StrContains(self._HOST),
833                            mox.StrContains(name),
834                            mox.StrContains('is_staged'))
835        dev_server.ImageServerBase.run_call(argument1).AndReturn('Success')
836        dev_server.ImageServerBase.run_call(argument2).AndReturn('True')
837
838        # Synchronous case requires a call to finish download.
839        self.mox.ReplayAll()
840        self.dev_server.finish_download(name)  # Raises on failure.
841        self.mox.VerifyAll()
842
843
844    def testErrorFinishDownload(self):
845        """Should call the dev server's finish download method using http, fail
846        gracefully."""
847        dev_server.ImageServerBase.run_call(mox.IgnoreArg()).AndRaise(E500)
848        self.mox.ReplayAll()
849        self.assertRaises(dev_server.DevServerException,
850                          self.dev_server.finish_download,
851                          '')
852
853
854    def testCmdErrorFinishDownload(self):
855        """Should call the dev server's finish download method using ssh,
856        retry finish_download when getting error.CmdError, raise exception
857        for urllib2.HTTPError."""
858        dev_server.ImageServerBase.run_call(
859                mox.IgnoreArg()).AndRaise(CMD_ERROR)
860        dev_server.ImageServerBase.run_call(
861                mox.IgnoreArg()).AndRaise(E500)
862        self.mox.ReplayAll()
863        self.assertRaises(dev_server.DevServerException,
864                          self.dev_server.finish_download,
865                          '')
866
867
868    def testListControlFiles(self):
869        """Should successfully list control files from the dev server."""
870        name = 'fake/build'
871        control_files = ['file/one', 'file/two']
872        argument = mox.And(mox.StrContains(self._HOST),
873                           mox.StrContains(name))
874        dev_server.ImageServerBase.run_call(
875                argument, readline=True).AndReturn(control_files)
876
877        self.mox.ReplayAll()
878        paths = self.dev_server.list_control_files(name)
879        self.assertEquals(len(paths), 2)
880        for f in control_files:
881            self.assertTrue(f in paths)
882
883
884    def testFailedListControlFiles(self):
885        """Should call the dev server's list-files method using http, get
886        exception."""
887        dev_server.ImageServerBase.run_call(
888                mox.IgnoreArg(), readline=True).AndRaise(E500)
889        self.mox.ReplayAll()
890        self.assertRaises(dev_server.DevServerException,
891                          self.dev_server.list_control_files,
892                          '')
893
894
895    def testExplodingListControlFiles(self):
896        """Should call the dev server's list-files method using http, get
897        exception."""
898        dev_server.ImageServerBase.run_call(
899                mox.IgnoreArg(), readline=True).AndRaise(E403)
900        self.mox.ReplayAll()
901        self.assertRaises(dev_server.DevServerException,
902                          self.dev_server.list_control_files,
903                          '')
904
905
906    def testCmdErrorListControlFiles(self):
907        """Should call the dev server's list-files method using ssh, retry
908        list_control_files when getting error.CmdError, raise exception for
909        urllib2.HTTPError."""
910        dev_server.ImageServerBase.run_call(
911                mox.IgnoreArg(), readline=True).AndRaise(CMD_ERROR)
912        dev_server.ImageServerBase.run_call(
913                mox.IgnoreArg(), readline=True).AndRaise(E500)
914        self.mox.ReplayAll()
915        self.assertRaises(dev_server.DevServerException,
916                          self.dev_server.list_control_files,
917                          '')
918
919    def testListSuiteControls(self):
920        """Should successfully list all contents of control files from the dev
921        server."""
922        name = 'fake/build'
923        control_contents = ['control file one', 'control file two']
924        argument = mox.And(mox.StrContains(self._HOST),
925                           mox.StrContains(name))
926        dev_server.ImageServerBase.run_call(
927                argument).AndReturn(json.dumps(control_contents))
928
929        self.mox.ReplayAll()
930        file_contents = self.dev_server.list_suite_controls(name)
931        self.assertEquals(len(file_contents), 2)
932        for f in control_contents:
933            self.assertTrue(f in file_contents)
934
935
936    def testFailedListSuiteControls(self):
937        """Should call the dev server's list_suite_controls method using http,
938        get exception."""
939        dev_server.ImageServerBase.run_call(
940                mox.IgnoreArg()).AndRaise(E500)
941        self.mox.ReplayAll()
942        self.assertRaises(dev_server.DevServerException,
943                          self.dev_server.list_suite_controls,
944                          '')
945
946
947    def testExplodingListSuiteControls(self):
948        """Should call the dev server's list_suite_controls method using http,
949        get exception."""
950        dev_server.ImageServerBase.run_call(
951                mox.IgnoreArg()).AndRaise(E403)
952        self.mox.ReplayAll()
953        self.assertRaises(dev_server.DevServerException,
954                          self.dev_server.list_suite_controls,
955                          '')
956
957
958    def testCmdErrorListSuiteControls(self):
959        """Should call the dev server's list_suite_controls method using ssh,
960        retry list_suite_controls when getting error.CmdError, raise exception
961        for urllib2.HTTPError."""
962        dev_server.ImageServerBase.run_call(
963                mox.IgnoreArg()).AndRaise(CMD_ERROR)
964        dev_server.ImageServerBase.run_call(
965                mox.IgnoreArg()).AndRaise(E500)
966        self.mox.ReplayAll()
967        self.assertRaises(dev_server.DevServerException,
968                          self.dev_server.list_suite_controls,
969                          '')
970
971
972    def testGetControlFile(self):
973        """Should successfully get a control file from the dev server."""
974        name = 'fake/build'
975        file = 'file/one'
976        contents = 'Multi-line\nControl File Contents\n'
977        argument = mox.And(mox.StrContains(self._HOST),
978                            mox.StrContains(name),
979                            mox.StrContains(file))
980        dev_server.ImageServerBase.run_call(argument).AndReturn(contents)
981
982        self.mox.ReplayAll()
983        self.assertEquals(self.dev_server.get_control_file(name, file),
984                          contents)
985
986
987    def testErrorGetControlFile(self):
988        """Should try to get the contents of a control file using http, get
989        exception."""
990        dev_server.ImageServerBase.run_call(mox.IgnoreArg()).AndRaise(E500)
991        self.mox.ReplayAll()
992        self.assertRaises(dev_server.DevServerException,
993                          self.dev_server.get_control_file,
994                          '', '')
995
996
997    def testForbiddenGetControlFile(self):
998        """Should try to get the contents of a control file using http, get
999        exception."""
1000        dev_server.ImageServerBase.run_call(mox.IgnoreArg()).AndRaise(E403)
1001        self.mox.ReplayAll()
1002        self.assertRaises(dev_server.DevServerException,
1003                          self.dev_server.get_control_file,
1004                          '', '')
1005
1006
1007    def testCmdErrorGetControlFile(self):
1008        """Should try to get the contents of a control file using ssh, retry
1009        get_control_file when getting error.CmdError, raise exception for
1010        urllib2.HTTPError."""
1011        dev_server.ImageServerBase.run_call(
1012                mox.IgnoreArg()).AndRaise(CMD_ERROR)
1013        dev_server.ImageServerBase.run_call(
1014                mox.IgnoreArg()).AndRaise(E500)
1015        self.mox.ReplayAll()
1016        self.assertRaises(dev_server.DevServerException,
1017                          self.dev_server.get_control_file,
1018                          '', '')
1019
1020
1021    def testGetLatestBuild(self):
1022        """Should successfully return a build for a given target."""
1023        self.mox.StubOutWithMock(dev_server.ImageServer, 'servers')
1024        self.mox.StubOutWithMock(dev_server.DevServer, 'devserver_healthy')
1025
1026        dev_server.ImageServer.servers().AndReturn([self._HOST])
1027        dev_server.ImageServer.devserver_healthy(self._HOST).AndReturn(True)
1028
1029        target = 'x86-generic-release'
1030        build_string = 'R18-1586.0.0-a1-b1514'
1031        argument = mox.And(mox.StrContains(self._HOST),
1032                           mox.StrContains(target))
1033        dev_server.ImageServerBase.run_call(argument).AndReturn(build_string)
1034
1035        self.mox.ReplayAll()
1036        build = dev_server.ImageServer.get_latest_build(target)
1037        self.assertEquals(build_string, build)
1038
1039
1040    def testGetLatestBuildWithManyDevservers(self):
1041        """Should successfully return newest build with multiple devservers."""
1042        self.mox.StubOutWithMock(dev_server.ImageServer, 'servers')
1043        self.mox.StubOutWithMock(dev_server.DevServer, 'devserver_healthy')
1044
1045        host0_expected = 'http://host0:8080'
1046        host1_expected = 'http://host1:8082'
1047
1048        dev_server.ImageServer.servers().MultipleTimes().AndReturn(
1049                [host0_expected, host1_expected])
1050
1051        dev_server.ImageServer.devserver_healthy(host0_expected).AndReturn(True)
1052        dev_server.ImageServer.devserver_healthy(host1_expected).AndReturn(True)
1053
1054        target = 'x86-generic-release'
1055        build_string1 = 'R9-1586.0.0-a1-b1514'
1056        build_string2 = 'R19-1586.0.0-a1-b3514'
1057        argument1 = mox.And(mox.StrContains(host0_expected),
1058                            mox.StrContains(target))
1059        argument2 = mox.And(mox.StrContains(host1_expected),
1060                            mox.StrContains(target))
1061        dev_server.ImageServerBase.run_call(argument1).AndReturn(build_string1)
1062        dev_server.ImageServerBase.run_call(argument2).AndReturn(build_string2)
1063
1064        self.mox.ReplayAll()
1065        build = dev_server.ImageServer.get_latest_build(target)
1066        self.assertEquals(build_string2, build)
1067
1068
1069    def testCrashesAreSetToTheCrashServer(self):
1070        """Should send symbolicate dump rpc calls to crash_server."""
1071        self.mox.ReplayAll()
1072        call = self.crash_server.build_call('symbolicate_dump')
1073        self.assertTrue(call.startswith(self._CRASH_HOST))
1074
1075
1076    def _stageTestHelper(self, artifacts=[], files=[], archive_url=None):
1077        """Helper to test combos of files/artifacts/urls with stage call."""
1078        expected_archive_url = archive_url
1079        if not archive_url:
1080            expected_archive_url = 'gs://my_default_url'
1081            self.mox.StubOutWithMock(dev_server, '_get_image_storage_server')
1082            dev_server._get_image_storage_server().AndReturn(
1083                'gs://my_default_url')
1084            name = 'fake/image'
1085        else:
1086            # This is embedded in the archive_url. Not needed.
1087            name = ''
1088
1089        argument1 = mox.And(mox.StrContains(expected_archive_url),
1090                            mox.StrContains(name),
1091                            mox.StrContains('artifacts=%s' %
1092                                            ','.join(artifacts)),
1093                            mox.StrContains('files=%s' % ','.join(files)),
1094                            mox.StrContains('stage?'))
1095        argument2 = mox.And(mox.StrContains(expected_archive_url),
1096                            mox.StrContains(name),
1097                            mox.StrContains('artifacts=%s' %
1098                                            ','.join(artifacts)),
1099                            mox.StrContains('files=%s' % ','.join(files)),
1100                            mox.StrContains('is_staged'))
1101        dev_server.ImageServerBase.run_call(argument1).AndReturn('Success')
1102        dev_server.ImageServerBase.run_call(argument2).AndReturn('True')
1103
1104        self.mox.ReplayAll()
1105        self.dev_server.stage_artifacts(name, artifacts, files, archive_url)
1106        self.mox.VerifyAll()
1107
1108
1109    def testStageArtifactsBasic(self):
1110        """Basic functionality to stage artifacts (similar to
1111        trigger_download)."""
1112        self._stageTestHelper(artifacts=['full_payload', 'stateful'])
1113
1114
1115    def testStageArtifactsBasicWithFiles(self):
1116        """Basic functionality to stage artifacts (similar to
1117        trigger_download)."""
1118        self._stageTestHelper(artifacts=['full_payload', 'stateful'],
1119                              files=['taco_bell.coupon'])
1120
1121
1122    def testStageArtifactsOnlyFiles(self):
1123        """Test staging of only file artifacts."""
1124        self._stageTestHelper(files=['tasty_taco_bell.coupon'])
1125
1126
1127    def testStageWithArchiveURL(self):
1128        """Basic functionality to stage artifacts (similar to
1129        trigger_download)."""
1130        self._stageTestHelper(files=['tasty_taco_bell.coupon'],
1131                              archive_url='gs://tacos_galore/my/dir')
1132
1133
1134    def testStagedFileUrl(self):
1135        """Sanity tests that the staged file url looks right."""
1136        devserver_label = 'x86-mario-release/R30-1234.0.0'
1137        url = self.dev_server.get_staged_file_url('stateful.tgz',
1138                                                  devserver_label)
1139        expected_url = '/'.join([self._HOST, 'static', devserver_label,
1140                                 'stateful.tgz'])
1141        self.assertEquals(url, expected_url)
1142
1143        devserver_label = 'something_crazy/that/you_MIGHT/hate'
1144        url = self.dev_server.get_staged_file_url('chromiumos_image.bin',
1145                                                  devserver_label)
1146        expected_url = '/'.join([self._HOST, 'static', devserver_label,
1147                                 'chromiumos_image.bin'])
1148        self.assertEquals(url, expected_url)
1149
1150
1151    def _StageTimeoutHelper(self):
1152        """Helper class for testing staging timeout."""
1153        self.mox.StubOutWithMock(dev_server.ImageServer, 'call_and_wait')
1154        dev_server.ImageServer.call_and_wait(
1155                call_name='stage',
1156                artifacts=mox.IgnoreArg(),
1157                files=mox.IgnoreArg(),
1158                archive_url=mox.IgnoreArg(),
1159                error_message=mox.IgnoreArg()).AndRaise(bin_utils.TimeoutError())
1160
1161
1162    def test_StageArtifactsTimeout(self):
1163        """Test DevServerException is raised when stage_artifacts timed out."""
1164        self._StageTimeoutHelper()
1165        self.mox.ReplayAll()
1166        self.assertRaises(dev_server.DevServerException,
1167                          self.dev_server.stage_artifacts,
1168                          image='fake/image', artifacts=['full_payload'])
1169        self.mox.VerifyAll()
1170
1171
1172    def test_TriggerDownloadTimeout(self):
1173        """Test DevServerException is raised when trigger_download timed out."""
1174        self._StageTimeoutHelper()
1175        self.mox.ReplayAll()
1176        self.assertRaises(dev_server.DevServerException,
1177                          self.dev_server.trigger_download,
1178                          image='fake/image')
1179        self.mox.VerifyAll()
1180
1181
1182    def test_FinishDownloadTimeout(self):
1183        """Test DevServerException is raised when finish_download timed out."""
1184        self._StageTimeoutHelper()
1185        self.mox.ReplayAll()
1186        self.assertRaises(dev_server.DevServerException,
1187                          self.dev_server.finish_download,
1188                          image='fake/image')
1189        self.mox.VerifyAll()
1190
1191
1192    def test_compare_load(self):
1193        """Test load comparison logic.
1194        """
1195        load_high_cpu = {'devserver': 'http://devserver_1:8082',
1196                         dev_server.DevServer.CPU_LOAD: 100.0,
1197                         dev_server.DevServer.NETWORK_IO: 1024*1024*1.0,
1198                         dev_server.DevServer.DISK_IO: 1024*1024.0}
1199        load_high_network = {'devserver': 'http://devserver_1:8082',
1200                             dev_server.DevServer.CPU_LOAD: 1.0,
1201                             dev_server.DevServer.NETWORK_IO: 1024*1024*100.0,
1202                             dev_server.DevServer.DISK_IO: 1024*1024*1.0}
1203        load_1 = {'devserver': 'http://devserver_1:8082',
1204                  dev_server.DevServer.CPU_LOAD: 1.0,
1205                  dev_server.DevServer.NETWORK_IO: 1024*1024*1.0,
1206                  dev_server.DevServer.DISK_IO: 1024*1024*2.0}
1207        load_2 = {'devserver': 'http://devserver_1:8082',
1208                  dev_server.DevServer.CPU_LOAD: 1.0,
1209                  dev_server.DevServer.NETWORK_IO: 1024*1024*1.0,
1210                  dev_server.DevServer.DISK_IO: 1024*1024*1.0}
1211        self.assertFalse(dev_server._is_load_healthy(load_high_cpu))
1212        self.assertFalse(dev_server._is_load_healthy(load_high_network))
1213        self.assertTrue(dev_server._compare_load(load_1, load_2) > 0)
1214
1215
1216    def _testSuccessfulTriggerDownloadAndroid(self, synchronous=True):
1217        """Call the dev server's download method with given synchronous
1218        setting.
1219
1220        @param synchronous: True to call the download method synchronously.
1221        """
1222        target = 'test_target'
1223        branch = 'test_branch'
1224        build_id = '123456'
1225        artifacts = android_utils.AndroidArtifacts.get_artifacts_for_reimage(
1226                None)
1227        self.mox.StubOutWithMock(dev_server.AndroidBuildServer,
1228                                 '_finish_download')
1229        argument1 = mox.And(mox.StrContains(self._HOST),
1230                            mox.StrContains(target),
1231                            mox.StrContains(branch),
1232                            mox.StrContains(build_id),
1233                            mox.StrContains('stage?'))
1234        argument2 = mox.And(mox.StrContains(self._HOST),
1235                            mox.StrContains(target),
1236                            mox.StrContains(branch),
1237                            mox.StrContains(build_id),
1238                            mox.StrContains('is_staged'))
1239        dev_server.ImageServerBase.run_call(argument1).AndReturn('Success')
1240        dev_server.ImageServerBase.run_call(argument2).AndReturn('True')
1241
1242        if synchronous:
1243            android_build_info = {'target': target,
1244                                  'build_id': build_id,
1245                                  'branch': branch}
1246            build = dev_server.ANDROID_BUILD_NAME_PATTERN % android_build_info
1247            self.android_dev_server._finish_download(
1248                    build, artifacts, '', target=target, build_id=build_id,
1249                    branch=branch)
1250
1251        # Synchronous case requires a call to finish download.
1252        self.mox.ReplayAll()
1253        self.android_dev_server.trigger_download(
1254                synchronous=synchronous, target=target, build_id=build_id,
1255                branch=branch)
1256        self.mox.VerifyAll()
1257
1258
1259    def testSuccessfulTriggerDownloadAndroidSync(self):
1260        """Call the dev server's download method with synchronous=True."""
1261        self._testSuccessfulTriggerDownloadAndroid(synchronous=True)
1262
1263
1264    def testSuccessfulTriggerDownloadAndroidAsync(self):
1265        """Call the dev server's download method with synchronous=False."""
1266        self._testSuccessfulTriggerDownloadAndroid(synchronous=False)
1267
1268
1269    def testGetUnrestrictedDevservers(self):
1270        """Test method get_unrestricted_devservers works as expected."""
1271        restricted_devserver = 'http://192.168.0.100:8080'
1272        unrestricted_devserver = 'http://172.1.1.3:8080'
1273        self.mox.StubOutWithMock(dev_server.ImageServer, 'servers')
1274        dev_server.ImageServer.servers().AndReturn([restricted_devserver,
1275                                                    unrestricted_devserver])
1276        self.mox.ReplayAll()
1277        self.assertEqual(dev_server.ImageServer.get_unrestricted_devservers(
1278                                [('192.168.0.0', 24)]),
1279                         [unrestricted_devserver])
1280
1281
1282    def testDevserverHealthy(self):
1283        """Test which types of connections that method devserver_healthy uses
1284        for different types of DevServer.
1285
1286        CrashServer always adopts DevServer.run_call.
1287        ImageServer and AndroidBuildServer use ImageServerBase.run_call.
1288        """
1289        argument = mox.StrContains(self._HOST)
1290
1291        # for testing CrashServer
1292        self.mox.StubOutWithMock(dev_server.DevServer, 'run_call')
1293        dev_server.DevServer.run_call(
1294                argument, timeout=mox.IgnoreArg()).AndReturn(
1295                        '{"free_disk": 1024}')
1296        # for testing ImageServer
1297        dev_server.ImageServerBase.run_call(
1298                argument, timeout=mox.IgnoreArg()).AndReturn(
1299                        '{"free_disk": 1024}')
1300        # for testing AndroidBuildServer
1301        dev_server.ImageServerBase.run_call(
1302                argument, timeout=mox.IgnoreArg()).AndReturn(
1303                        '{"free_disk": 1024}')
1304
1305        self.mox.ReplayAll()
1306        self.assertTrue(dev_server.CrashServer.devserver_healthy(self._HOST))
1307        self.assertTrue(dev_server.ImageServer.devserver_healthy(self._HOST))
1308        self.assertTrue(
1309                dev_server.AndroidBuildServer.devserver_healthy(self._HOST))
1310
1311
1312    def testLocateFile(self):
1313        """Test locating files for AndriodBuildServer."""
1314        file_name = 'fake_file'
1315        artifacts=['full_payload', 'stateful']
1316        build = 'fake_build'
1317        argument = mox.And(mox.StrContains(file_name),
1318                            mox.StrContains(build),
1319                            mox.StrContains('locate_file'))
1320        dev_server.ImageServerBase.run_call(argument).AndReturn('file_path')
1321
1322        self.mox.ReplayAll()
1323        file_location = 'http://nothing/static/fake_build/file_path'
1324        self.assertEqual(self.android_dev_server.locate_file(
1325                file_name, artifacts, build, None), file_location)
1326
1327    def testCmdErrorLocateFile(self):
1328        """Test locating files for AndriodBuildServer for retry
1329        error.CmdError, and raise urllib2.URLError."""
1330        dev_server.ImageServerBase.run_call(
1331                mox.IgnoreArg()).AndRaise(CMD_ERROR)
1332        dev_server.ImageServerBase.run_call(
1333                mox.IgnoreArg()).AndRaise(E500)
1334        self.mox.ReplayAll()
1335        self.assertRaises(dev_server.DevServerException,
1336                          self.dev_server.trigger_download,
1337                          '')
1338
1339
1340    def testGetAvailableDevserversForCrashServer(self):
1341        """Test method get_available_devservers for CrashServer."""
1342        crash_servers = ['http://crash_servers1:8080']
1343        host = '127.0.0.1'
1344        self.mox.StubOutWithMock(dev_server.CrashServer, 'servers')
1345        dev_server.CrashServer.servers().AndReturn(crash_servers)
1346        self.mox.ReplayAll()
1347        self.assertEqual(dev_server.CrashServer.get_available_devservers(host),
1348                        (crash_servers, False))
1349
1350
1351    def testGetAvailableDevserversForImageServer(self):
1352        """Test method get_available_devservers for ImageServer."""
1353        unrestricted_host = '100.0.0.99'
1354        unrestricted_servers = ['http://100.0.0.10:8080',
1355                                'http://128.0.0.10:8080']
1356        same_subnet_unrestricted_servers = ['http://100.0.0.10:8080']
1357        restricted_host = '127.0.0.99'
1358        restricted_servers = ['http://127.0.0.10:8080']
1359        all_servers = unrestricted_servers + restricted_servers
1360        # Set restricted subnets
1361        restricted_subnets = [('127.0.0.0', 24)]
1362        self.mox.StubOutWithMock(dev_server.ImageServerBase, 'servers')
1363        dev_server.ImageServerBase.servers().MultipleTimes().AndReturn(
1364                all_servers)
1365        self.mox.ReplayAll()
1366        # dut in unrestricted subnet shall be offered devserver in the same
1367        # subnet first, and allow retry.
1368        self.assertEqual(
1369                dev_server.ImageServer.get_available_devservers(
1370                        unrestricted_host, True, restricted_subnets),
1371                (same_subnet_unrestricted_servers, True))
1372
1373        # If prefer_local_devserver is set to False, allow any devserver in
1374        # unrestricted subet to be available, and retry is not allowed.
1375        self.assertEqual(
1376                dev_server.ImageServer.get_available_devservers(
1377                        unrestricted_host, False, restricted_subnets),
1378                (unrestricted_servers, False))
1379
1380        # When no hostname is specified, all devservers in unrestricted subnets
1381        # should be considered, and retry is not allowed.
1382        self.assertEqual(
1383                dev_server.ImageServer.get_available_devservers(
1384                        None, True, restricted_subnets),
1385                (unrestricted_servers, False))
1386
1387        # dut in restricted subnet should only be offered devserver in the
1388        # same restricted subnet, and retry is not allowed.
1389        self.assertEqual(
1390                dev_server.ImageServer.get_available_devservers(
1391                        restricted_host, True, restricted_subnets),
1392                (restricted_servers, False))
1393
1394
1395if __name__ == "__main__":
1396    unittest.main()
1397