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