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